Android 打造形形色色的進度條 實現可以如此簡單
1、概述
最近需要用進度條,秉著不重複造輪子的原則,上github上搜索了一番,看了幾個覺得比較好看的ProgressBar,比如:daimajia的等。簡單看了下程式碼,基本都是繼承自View,徹徹底底的自定義了一個進度條。盯著那絢麗滾動條,忽然覺得,為什麼要通過View去寫一個滾動條,系統已經提供了ProgressBar以及屬於它的特性,我們沒必要重新去構建一個,但是系統的又比較醜,不同版本變現還不一定一樣。那麼得出我們的目標:改變系統ProgressBar的樣子。
對沒錯,我們沒有必要去從0打造一個ProgressBar,人家雖然長的不好看,但是特性以及穩定性還是剛剛的,我們只需要為其整下容就ok了。
說到整容,大家都知道我們的控制元件是通過onDraw()畫出來的,那麼我們只需要去覆蓋它的onDraw()方法,自己寫下就ok 。
對了,我建立了一個微信公眾號,歡迎關注,左邊欄目上掃一掃即可。
接下來,我們貼下效果圖:
2、效果圖
1、橫向的進度條
2、圓形的進度條
沒錯,這就是我們的進度條效果,橫向的模仿了daimajia的進度條樣子。不過我們繼承子ProgressBar,簡單的為其整個容,程式碼清晰易懂 。為什麼說,易懂呢?
橫向那個進度條,大家會drawLine()和drawText()吧,那麼通過getWidth()拿到控制元件的寬度,再通過getProgress()拿到進度,按比例控制繪製線的長短,字的位置還不是分分鐘的事。
3、實現
橫向的滾動條繪製肯定需要一些屬性,比如已/未到達進度的顏色、寬度,文字的顏色、大小等。
本來呢,我是想通過系統ProgressBar的progressDrawable,從裡面提取一些屬性完成繪製需要的引數的。但是,最終呢,反而讓程式碼變得複雜。所以最終還是改用自定義屬性。 說道自定義屬性,大家應該已經不陌生了。
1、HorizontalProgressBarWithNumber
1、自定義屬性
values/attr_progress_bar.xml:
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="HorizontalProgressBarWithNumber"> <attr name="progress_unreached_color" format="color" /> <attr name="progress_reached_color" format="color" /> <attr name="progress_reached_bar_height" format="dimension" /> <attr name="progress_unreached_bar_height" format="dimension" /> <attr name="progress_text_size" format="dimension" /> <attr name="progress_text_color" format="color" /> <attr name="progress_text_offset" format="dimension" /> <attr name="progress_text_visibility" format="enum"> <enum name="visible" value="0" /> <enum name="invisible" value="1" /> </attr> </declare-styleable> <declare-styleable name="RoundProgressBarWidthNumber"> <attr name="radius" format="dimension" /> </declare-styleable> </resources>
2、構造中獲取
public class HorizontalProgressBarWithNumber extends ProgressBar
{
private static final int DEFAULT_TEXT_SIZE = 10;
private static final int DEFAULT_TEXT_COLOR = 0XFFFC00D1;
private static final int DEFAULT_COLOR_UNREACHED_COLOR = 0xFFd3d6da;
private static final int DEFAULT_HEIGHT_REACHED_PROGRESS_BAR = 2;
private static final int DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR = 2;
private static final int DEFAULT_SIZE_TEXT_OFFSET = 10;
/**
* painter of all drawing things
*/
protected Paint mPaint = new Paint();
/**
* color of progress number
*/
protected int mTextColor = DEFAULT_TEXT_COLOR;
/**
* size of text (sp)
*/
protected int mTextSize = sp2px(DEFAULT_TEXT_SIZE);
/**
* offset of draw progress
*/
protected int mTextOffset = dp2px(DEFAULT_SIZE_TEXT_OFFSET);
/**
* height of reached progress bar
*/
protected int mReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_REACHED_PROGRESS_BAR);
/**
* color of reached bar
*/
protected int mReachedBarColor = DEFAULT_TEXT_COLOR;
/**
* color of unreached bar
*/
protected int mUnReachedBarColor = DEFAULT_COLOR_UNREACHED_COLOR;
/**
* height of unreached progress bar
*/
protected int mUnReachedProgressBarHeight = dp2px(DEFAULT_HEIGHT_UNREACHED_PROGRESS_BAR);
/**
* view width except padding
*/
protected int mRealWidth;
protected boolean mIfDrawText = true;
protected static final int VISIBLE = 0;
public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs)
{
this(context, attrs, 0);
}
public HorizontalProgressBarWithNumber(Context context, AttributeSet attrs,
int defStyle)
{
super(context, attrs, defStyle);
setHorizontalScrollBarEnabled(true);
obtainStyledAttributes(attrs);
mPaint.setTextSize(mTextSize);
mPaint.setColor(mTextColor);
}
/**
* get the styled attributes
*
* @param attrs
*/
private void obtainStyledAttributes(AttributeSet attrs)
{
// init values from custom attributes
final TypedArray attributes = getContext().obtainStyledAttributes(
attrs, R.styleable.HorizontalProgressBarWithNumber);
mTextColor = attributes
.getColor(
R.styleable.HorizontalProgressBarWithNumber_progress_text_color,
DEFAULT_TEXT_COLOR);
mTextSize = (int) attributes.getDimension(
R.styleable.HorizontalProgressBarWithNumber_progress_text_size,
mTextSize);
mReachedBarColor = attributes
.getColor(
R.styleable.HorizontalProgressBarWithNumber_progress_reached_color,
mTextColor);
mUnReachedBarColor = attributes
.getColor(
R.styleable.HorizontalProgressBarWithNumber_progress_unreached_color,
DEFAULT_COLOR_UNREACHED_COLOR);
mReachedProgressBarHeight = (int) attributes
.getDimension(
R.styleable.HorizontalProgressBarWithNumber_progress_reached_bar_height,
mReachedProgressBarHeight);
mUnReachedProgressBarHeight = (int) attributes
.getDimension(
R.styleable.HorizontalProgressBarWithNumber_progress_unreached_bar_height,
mUnReachedProgressBarHeight);
mTextOffset = (int) attributes
.getDimension(
R.styleable.HorizontalProgressBarWithNumber_progress_text_offset,
mTextOffset);
int textVisible = attributes
.getInt(R.styleable.HorizontalProgressBarWithNumber_progress_text_visibility,
VISIBLE);
if (textVisible != VISIBLE)
{
mIfDrawText = false;
}
attributes.recycle();
}
嗯,看起來程式碼挺長,其實都是在獲取自定義屬性,沒什麼技術含量。
3、onMeasure
剛才不是出onDraw裡面寫寫就行了麼,為什麼要改onMeasure呢,主要是因為我們所有的屬性比如進度條寬度讓使用者自定義了,所以我們的測量也得稍微變下。
@Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec)
{
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode != MeasureSpec.EXACTLY)
{
float textHeight = (mPaint.descent() + mPaint.ascent());
int exceptHeight = (int) (getPaddingTop() + getPaddingBottom() + Math
.max(Math.max(mReachedProgressBarHeight,
mUnReachedProgressBarHeight), Math.abs(textHeight)));
heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight,
MeasureSpec.EXACTLY);
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
寬度我們不變,所以的自定義屬性不涉及寬度,高度呢,只考慮不是EXACTLY的情況(使用者明確指定了,我們就不管了),根據padding和進度條寬度算出自己想要的,如果非EXACTLY下,我們進行exceptHeight封裝,傳入給控制元件進行測量高度。
測量完,就到我們的onDraw了~~~
4、onDraw
@Override
protected synchronized void onDraw(Canvas canvas)
{
canvas.save();
//畫筆平移到指定paddingLeft, getHeight() / 2位置,注意以後座標都為以此為0,0
canvas.translate(getPaddingLeft(), getHeight() / 2);
boolean noNeedBg = false;
//當前進度和總值的比例
float radio = getProgress() * 1.0f / getMax();
//已到達的寬度
float progressPosX = (int) (mRealWidth * radio);
//繪製的文字
String text = getProgress() + "%";
//拿到字型的寬度和高度
float textWidth = mPaint.measureText(text);
float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;
//如果到達最後,則未到達的進度條不需要繪製
if (progressPosX + textWidth > mRealWidth)
{
progressPosX = mRealWidth - textWidth;
noNeedBg = true;
}
// 繪製已到達的進度
float endX = progressPosX - mTextOffset / 2;
if (endX > 0)
{
mPaint.setColor(mReachedBarColor);
mPaint.setStrokeWidth(mReachedProgressBarHeight);
canvas.drawLine(0, 0, endX, 0, mPaint);
}
// 繪製文字
if (mIfDrawText)
{
mPaint.setColor(mTextColor);
canvas.drawText(text, progressPosX, -textHeight, mPaint);
}
// 繪製未到達的進度條
if (!noNeedBg)
{
float start = progressPosX + mTextOffset / 2 + textWidth;
mPaint.setColor(mUnReachedBarColor);
mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
canvas.drawLine(start, 0, mRealWidth, 0, mPaint);
}
canvas.restore();
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
mRealWidth = w - getPaddingRight() - getPaddingLeft();
}
其實核心方法就是onDraw了,但是呢,onDraw也很簡單,繪製線、繪製文字、繪製線,結束。
還有兩個簡單的輔助方法:
/**
* dp 2 px
*
* @param dpVal
*/
protected int dp2px(int dpVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dpVal, getResources().getDisplayMetrics());
}
/**
* sp 2 px
*
* @param spVal
* @return
*/
protected int sp2px(int spVal)
{
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
spVal, getResources().getDisplayMetrics());
}
好了,到此我們的橫向進度就結束了,是不是很簡單~~如果你是自定義View,你還得考慮progress的更新,考慮狀態的銷燬與恢復等等複雜的東西。
接下來看我們的RoundProgressBarWidthNumber圓形的進度條。
2、RoundProgressBarWidthNumber
圓形的進度條和橫向的進度條基本變數都是一致的,於是我就讓RoundProgressBarWidthNumber extends HorizontalProgressBarWithNumber 了。
然後需要改變的就是測量和onDraw了:
完整程式碼:
package com.zhy.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint.Cap;
import android.graphics.Paint.Style;
import android.graphics.RectF;
import android.util.AttributeSet;
import com.zhy.library.view.R;
public class RoundProgressBarWidthNumber extends
HorizontalProgressBarWithNumber {
/**
* mRadius of view
*/
private int mRadius = dp2px(30);
public RoundProgressBarWidthNumber(Context context) {
this(context, null);
}
public RoundProgressBarWidthNumber(Context context, AttributeSet attrs) {
super(context, attrs);
mReachedProgressBarHeight = (int) (mUnReachedProgressBarHeight * 2.5f);
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.RoundProgressBarWidthNumber);
mRadius = (int) ta.getDimension(
R.styleable.RoundProgressBarWidthNumber_radius, mRadius);
ta.recycle();
mTextSize = sp2px(14);
mPaint.setStyle(Style.STROKE);
mPaint.setAntiAlias(true);
mPaint.setDither(true);
mPaint.setStrokeCap(Cap.ROUND);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int paintWidth = Math.max(mReachedProgressBarHeight,
mUnReachedProgressBarHeight);
if (heightMode != MeasureSpec.EXACTLY) {
int exceptHeight = (int) (getPaddingTop() + getPaddingBottom()
+ mRadius * 2 + paintWidth);
heightMeasureSpec = MeasureSpec.makeMeasureSpec(exceptHeight,
MeasureSpec.EXACTLY);
}
if (widthMode != MeasureSpec.EXACTLY) {
int exceptWidth = (int) (getPaddingLeft() + getPaddingRight()
+ mRadius * 2 + paintWidth);
widthMeasureSpec = MeasureSpec.makeMeasureSpec(exceptWidth,
MeasureSpec.EXACTLY);
}
super.onMeasure(heightMeasureSpec, heightMeasureSpec);
}
@Override
protected synchronized void onDraw(Canvas canvas) {
String text = getProgress() + "%";
// mPaint.getTextBounds(text, 0, text.length(), mTextBound);
float textWidth = mPaint.measureText(text);
float textHeight = (mPaint.descent() + mPaint.ascent()) / 2;
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
mPaint.setStyle(Style.STROKE);
// draw unreaded bar
mPaint.setColor(mUnReachedBarColor);
mPaint.setStrokeWidth(mUnReachedProgressBarHeight);
canvas.drawCircle(mRadius, mRadius, mRadius, mPaint);
// draw reached bar
mPaint.setColor(mReachedBarColor);
mPaint.setStrokeWidth(mReachedProgressBarHeight);
float sweepAngle = getProgress() * 1.0f / getMax() * 360;
canvas.drawArc(new RectF(0, 0, mRadius * 2, mRadius * 2), 0,
sweepAngle, false, mPaint);
// draw text
mPaint.setStyle(Style.FILL);
canvas.drawText(text, mRadius - textWidth / 2, mRadius - textHeight,
mPaint);
canvas.restore();
}
}
首先獲取它的專有屬性mRadius,然後根據此屬性去測量,測量完成繪製;
繪製的過程呢?
先繪製一個細一點的圓,然後繪製一個粗一點的弧度,二者疊在一起就行。文字呢,繪製在中間~~~總體,沒什麼程式碼量。
好了,兩個進度條就到這了,是不是發現簡單很多。總體設計上,存在些問題,如果抽取一個BaseProgressBar用於獲取公共的屬性;然後不同樣子的進度條繼承分別實現自己的測量和樣子,這樣結構可能會清晰些~~~
4、使用
佈局檔案
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:zhy="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="25dp" >
<com.zhy.view.HorizontalProgressBarWithNumber
android:id="@+id/id_progressbar01"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dip"
android:padding="5dp" />
<com.zhy.view.HorizontalProgressBarWithNumber
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dip"
android:padding="5dp"
android:progress="50"
zhy:progress_text_color="#ffF53B03"
zhy:progress_unreached_color="#ffF7C6B7" />
<com.zhy.view.RoundProgressBarWidthNumber
android:id="@+id/id_progress02"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dip"
android:padding="5dp"
android:progress="30" />
<com.zhy.view.RoundProgressBarWidthNumber
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="50dip"
android:padding="5dp"
android:progress="50"
zhy:progress_reached_bar_height="20dp"
zhy:progress_text_color="#ffF53B03"
zhy:radius="60dp" />
</LinearLayout>
</ScrollView>
MainActivity
package com.zhy.sample.progressbar;
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import com.zhy.annotation.Log;
import com.zhy.view.HorizontalProgressBarWithNumber;
public class MainActivity extends Activity {
private HorizontalProgressBarWithNumber mProgressBar;
private static final int MSG_PROGRESS_UPDATE = 0x110;
private Handler mHandler = new Handler() {
@Log
public void handleMessage(android.os.Message msg) {
int progress = mProgressBar.getProgress();
mProgressBar.setProgress(++progress);
if (progress >= 100) {
mHandler.removeMessages(MSG_PROGRESS_UPDATE);
}
mHandler.sendEmptyMessageDelayed(MSG_PROGRESS_UPDATE, 100);
};
};
@Log
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mProgressBar = (HorizontalProgressBarWithNumber) findViewById(R.id.id_progressbar01);
mHandler.sendEmptyMessage(MSG_PROGRESS_UPDATE);
}
}
最後,本篇部落格的目的呢?就是為了說下,類似ProgressBar這樣的控制元件,如果你只是想去改變顯示的樣子,完全沒必要從0去建立,複寫onDraw即可,當然是個人觀點,提出供大家參考。
相關推薦
Android 打造形形色色的進度條 實現可以如此簡單
1、概述 最近需要用進度條,秉著不重複造輪子的原則,上github上搜索了一番,看了幾個覺得比較好看的ProgressBar,比如:daimajia的等。簡單看了下程式碼,基本都是繼承自View,徹徹底底的自定義了一個進度條。盯著那絢麗滾動條,忽然覺得,為什麼要通過View去寫
Android 打造形形色色的進度條 實現可以如此簡單
轉載請標明出處:http://blog.csdn.net/lmj623565791/article/details/43371299 ,本文出自:【張鴻洋的部落格】1、概述最近需要用進度條,秉著不重複造輪子的原則,上github上搜索了一番,看了幾個覺得比較好看的Progre
Android原生繪圖進度條+簡單自定義屬性程式碼生成器
零、前言 1.感覺切拼字串是個很有意思的事,好的拼接方式可以自動生成一些很實用的東西 2.本文自定義控制元件並不是很高大上的東西,目的在於計錄自定義控制元件的書寫規範與行文流程 3.建議大家自定義控制元件時自定義屬性有自己專屬字首,有利無害,何樂不為 4.本文是根據鴻洋在慕課網上的教程敲的:詳見,自己
linux—進度條彩色版簡單實現
一、緩衝區理解: 1.無緩衝:將所得到的資訊馬上顯示出來。 2.行緩衝:輸入輸出遇到換行才執行的I/O操作,比如鍵盤操作。 3.全緩衝:輸入輸出寫滿緩衝區蔡執行I/O操作。比如磁碟讀寫。 當我們在實現的時候需要使用fflush(stdout)來重新整理緩衝區,以
【Android】一、Progress進度條實現的三種方式:主執行緒實現,Service載入,動態建立
前言 更新版本,上傳資料到服務端,都是需要進度顯示的,Android進度顯示兩種方式 ProgressDialog 和 ProgressBar 新版本中ProgressDialog不被推薦使用,所以專案採用ProgressBar 分為三種實現方式: 1、MainAct
Android自定義圓形進度條實現程式碼
基本思路是這樣的: 1.首先繪製一個實心圓 2.繪製一個白色實心的正方形,遮住實心圓 3.在圓的中心動態繪製當前進度的百分比字元 4.繪製一個與之前實心圓相同顏色的空心圓 5.逐漸改變當前的百分比 6.根據百分比,逐漸改變正方形的大小,逐漸減小正方形的底部y軸的座標,不斷重繪
Android檔案下載進度條的實現
package com.pocketdigi.download; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL;
Android花樣loading進度條(二)-簡單環形進度條
背景 Android花樣loading進度條系列文章主要講解如何自定義所需的進度條,包括水平、圓形、環形、圓弧形、不規則形狀等。 本篇我們從圓形進度條講起,講簡單形式的環形進度條,只有進度色彩,沒有進度文字,主要是使用Canvas繪製圓和圓弧。 效果
Android閃屏頁圓形倒計時進度條實現
前言 現在我們的App中基本都會有閃屏頁面,而閃屏頁中大多又都會加入廣告資訊或者我們自己logo等宣傳圖片的展示,類似如下效果: 思路 使用自定義View,通過View的重繪方法Invalidate()在onDraw()中不斷繪製不同弧度的圓弧來改
Android 自定義進度條ColorfulProgressBar,原理簡單、效果很棒
Android-ColorfulProgressBar 簡介: 這是一個自定義的Progressbar,效果看著還行吧,滾動的雙色斜條作為進度條,有點類似Bootstrap風格。原生Progress的基本操作都有,自行觀摩我的原始碼吧,挺簡單的。
Android 仿微信載入H5頁面進度條實現
前言 Android中WebView打卡前端頁面時受到網路環境,頁面內容大小的影響有時候會讓使用者等待很久。顯示一個載入進度條可以提升很大的體驗。微信內訪問H5頁面載入效果不錯,效仿著寫了一個。 1
jquery 簡單的進度條實現程式碼
效果圖 需要用到的圖片: 背景圖片: 進度顯示圖片: 網頁結構: 複製程式碼 程式碼如下: <div id="center"> <div id="message"></div> <div id="loading">
Android更新帶進度條的通知欄
text linear archive last sys package 初始 麻煩 httputils 在網上查詢了下。Android版本號更新通知欄帶進度條,醉了,基本都是復制過來。有的代碼不全,連源代碼下載都沒有。有下載也須要積分,還不能用,真黑心啊!!之前自己也
aNDROID自定義進度條顏色
定義 hao123 自定義進度條 com list andro 顏色 androi baidu DIaLOGFRaGMENT%E9%97%AE%E9%A2%98%E6%B1%82%E8%A7%A3 http://music.baidu.com/songlist/49564
javascript使用ajax下載文件進度條實現
javascript使用ajax下載文件代碼: <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="te
PHP 遠程文件下載的進度條實現
其他 compact AS lose 信息 tint spinner ajax style download.php 1 <?php 2 // 當前文件:download.php 3 4 $action = @$_GET[‘action‘]; 5
進度條 ProgressBar的簡單運用
進度條 ProgressBar 1 常用屬性 style=”?android:attr/progressBarStyleHorizontal” 預設為圓形 android:progress=”33” android:max=”100” 執行緒休眠 Thread.sleep(100
android:漸變色進度條(圓環)
public class CircleView extends View { private Paint paint;//畫筆 private Paint textPaint;//文言畫筆 private RectF oval;//rectF物件(圓環) priv
頁面載入進度條實現——readyState和onreadystatechange
document.readyState 屬性返回當前文件的狀態(載入中……)。 共有四種取值: 1,uninitialized - 還未開始載入 uninitialized 英 [ʌnɪ'nɪʃlaɪzd] 未初始化 2,loading
自定義控制元件Android圓環進度條
Android自定義控制元件學習之圓環進度條,學習了一段時間,自定義控制元件,對一些自定義所需要的Api,基本上也掌握了,但是對於比較複雜的自定義控制元件,他們的座標計算 一直比較噁心(可能,我數學比較差~),記錄自定義控制元件學習過程。 對於圓環進度條的自定義