Android簡易的圓形進度條
自定義View基礎(chǔ)入門看2個系列文章,非常優(yōu)秀的文章悍引。
安卓自定義View教程目錄
HenCoder Android 開發(fā)進階: 自定義 View 1-2 Paint 詳解
-
最近因為項目需要橡淆,點擊按鈕锡宋,實現(xiàn)轉(zhuǎn)一圈的進度條萎战,感覺需求比較簡單瘫证,就自己做了一個自定義View,很簡易揉阎,分享給大家。
功力有限背捌,只能一步步完成功能毙籽,一步步改代碼,最后完成毡庆。
-
先看看效果坑赡,圖片有壓縮,丟幀么抗,不是很清晰
- 先初始化畫筆毅否,畫個圓圈
public class CircleProgressView extends View
{
//控件的寬
private int width;
//控件的高
private int height;
//區(qū)域
private RectF rectF;
//底層畫筆
private Paint paint_base_progress;
//進度條的畫筆
private Paint paint_progress;
//畫筆寬度
private int strokeWidth = ConvertUtils.dp2px(5);
//繪制半徑
private int radius;
public CircleProgressView(Context context)
{
this(context, null);
}
public CircleProgressView(Context context, @Nullable AttributeSet attrs)
{
this(context, attrs, 0);
}
/**
* 由于要使用xml配置控件屬性,所以要3個參數(shù)構(gòu)造方法
*
* @param context
* @param attrs
* @param defStyleAttr
*/
public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr)
{
super(context, attrs, defStyleAttr);
paint_base_progress = initPaint();
paint_progress = initPaint();
}
/**
* 初始化畫筆
*/
private Paint initPaint()
{
Paint paint = new Paint();
paint.setColor(Color.BLACK);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(strokeWidth);
//筆畫圓潤,用Paint.Cap.ROUND
paint.setStrokeCap(Paint.Cap.BUTT);
//抗鋸齒
paint.setAntiAlias(true);
return paint;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
width = MeasureSpec.getSize(widthMeasureSpec);
height = MeasureSpec.getSize(heightMeasureSpec);
if (rectF == null)
{
//半徑
if (radius == 0)
{
radius = (width > height ? width : height) / 2;
}
rectF = new RectF(width / 2 - radius + strokeWidth / 2
, height / 2 - radius + strokeWidth / 2
, width / 2 + radius - strokeWidth / 2
, height / 2 + radius - strokeWidth / 2);
}
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
//因為圓開始角度是x軸蝇刀,所以給他旋轉(zhuǎn)270°
canvas.rotate(270f, width / 2, height / 2);
canvas.drawArc(rectF, 0f, 360f, false, paint_base_progress);
}
}
- 這里藍色就是RectF的大小螟加,黑色就是圓環(huán),畫筆其實是中間經(jīng)過內(nèi)容的吞琐,所以捆探,在確定RectF大小的時候,要算上畫筆的一半站粟。
- 接下來給這個圓圈加個漸變色吧
//底層掃描漸變色
private Shader shader_base_progress;
//底層條淺的色
private int base_progress_light_color;
//底層條深的色
private int base_progress_height_color;
//底層顏色數(shù)組
private int base_progress_colors[];
//底層顏色分配
private float base_progress_positions[];
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
...
if (shader_base_progress == null)
{
//初始化顏色,如果不設(shè)置顏色參數(shù)黍图,就會使用這個顏色參數(shù)
base_progress_light_color = Color.WHITE;
base_progress_height_color = Color.BLACK;
base_progress_colors = new int[]{base_progress_light_color, base_progress_height_color, base_progress_light_color};
//這個參數(shù)就是顏色的分配,第一種顏色到0.1的位置卒蘸,第二種到0.9的位置雌隅,第三種到1的位置,不要超過1缸沃。
base_progress_positions = new float[]{0.1f, 0.95f, 1f};
shader_base_progress = new SweepGradient(width / 2, height / 2
, base_progress_colors, base_progress_positions);
paint_base_progress.setShader(shader_base_progress);
}
}
base_progress_colors = new int[]{ Color.WHITE, Color.BLACK, Color.RED};
//這個參數(shù)就是顏色的分配恰起,第一種顏色到0.1的位置,第二種到0.9的位置趾牧,第三種到1的位置检盼,不要超過1。
base_progress_positions = new float[]{0.1f, 0.8f, 1f};
- 可以看出參數(shù)變化之后翘单,顏色配色比例不同的效果吨枉,為了過渡的自然蹦渣,所以我一般喜歡最后的0.05段補上和第一段一樣的顏色。
- 這個是底色貌亭,然后進度條的寫法和這個底色一樣樣柬唯,只是顏色變一變就行啦。
- 下一步圃庭,想辦法讓這個進度條一點點轉(zhuǎn)動塞茅,不是整個旋轉(zhuǎn)愧沟,底層的圓不動褪那,外層的進度條一點點加隔嫡,需要使用Handler
//順時針
private boolean progress_clockwise = true;
//速度,刷新頻率最快60幀,參數(shù)為每次刷新的間隔书在,單位:毫秒
private long speed = 17;
//轉(zhuǎn)一圈的周期灰伟,單位:毫秒
private long period = 3000;
//循環(huán)
private boolean progress_recycler = false;
//暫停
private boolean progress_pause = false;
//開始
private static final int START = 1111;
//結(jié)束
private static final int COMPLETE = 1000;
private Handler handler = new Handler(new Handler.Callback()
{
@Override
public boolean handleMessage(Message msg)
{
switch (msg.what)
{
case START:
//順時針轉(zhuǎn)
if (progress_clockwise)
{
//每圈360°不變,共period辣么長的ms儒旬,360f/period算出每ms轉(zhuǎn)的角度栏账,再用結(jié)果*speek就是每次刷新轉(zhuǎn)的角度
progress_angle += 360f / period * speed ;
}
//逆時針轉(zhuǎn)
else
{
progress_angle -= 360f / period * speed ;
}
//轉(zhuǎn)滿一圈就停止,并且恢復(fù)底色,絕對值>360
if (Math.abs(progress_angle) > 360)
{
drawing = false;
handler.sendEmptyMessage(COMPLETE);
} else
{
drawing = true;
invalidate();
//我試過义矛,1ms发笔,10ms,速度都是一樣凉翻,但是100ms了讨,1000ms,就明顯不一樣
//打游戲打得多制轰,我懷疑是刷新頻率最快60幀前计,約16.67ms≈17ms,果然如此
handler.sendEmptyMessageDelayed(START, speed );
}
break;
case COMPLETE:
invalidate();
break;
}
return false;
}
});
/**
* 進度條開始
*/
public void startProgress()
{
progress_angle = 0f;
//每次都清除
handler.removeMessages(START);
handler.sendEmptyMessageDelayed(START, speed);
}
@Override
protected void onDraw(Canvas canvas)
{
...
if (drawing)
{
canvas.drawArc(rectF, 0f, base_angle, false, paint_base_progress);
canvas.drawArc(rectF, 0f, progress_angle, false, paint_progress);
} else
{
canvas.drawArc(rectF, 0f, base_angle, false, paint_base_progress);
}
}
- 只要調(diào)用startProgress 方法垃杖,就會開始轉(zhuǎn)啦男杈。停止也簡單,只要handler.remove掉就停下來了调俘。
- 個人喜好寫一些能用的工具伶棒。。彩库。肤无。所以要寫成可以用xml配置或者動態(tài)代碼變化的控件,而不是每次用的時候骇钦,改View宛渐。
-
res/values/ 目錄下新建一個attrs.xml文件。
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- 底層進度條淺顏色 -->
<attr name="base_progress_light_color" format="color" />
<!-- 進度條寬度 -->
<attr name="progress_width" format="dimension" />
<!-- 順時針轉(zhuǎn) -->
<attr name="progress_clockwise" format="boolean" />
<!-- 周期 -->
<attr name="progress_period" format="integer" />
<!-- 循環(huán)播放 -->
<attr name="progress_recycler" format="boolean"/>
<declare-styleable name="CircleProgressView">
<!-- 底層進度條淺顏色 -->
<attr name="base_progress_light_color" />
<!-- 進度條寬度 -->
<attr name="progress_width" />
<!-- 順時針轉(zhuǎn) -->
<attr name="progress_clockwise" />
<!-- 周期 -->
<attr name="progress_period" />
<!-- 循環(huán)播放 -->
<attr name="progress_recycler" />
</declare-styleable>
</resources>
這里列出幾個,format的類型很多種窥翩,color,就是可以用colors.xml下的資源业岁,dimension就是長寬,dp
寇蚊,px之類的參數(shù)笔时,所以以此類推,其他類型大家也很好理解的幔荒。定義好參數(shù)之后糊闽,我們就在三個參數(shù)的構(gòu)造方法那里獲取就可以了梳玫;因為xml不一定有配置一些參數(shù)爹梁,這邊也不會走對應(yīng)的初始化,所以關(guān)鍵參數(shù)最好弄一些默認值提澎。
public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr)
{
.....
//獲取自定義樣式的屬性
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CircleProgressView, defStyleAttr, 0);
for (int i = 0; i < typedArray.getIndexCount(); i++)
{
int attr = typedArray.getIndex(i);
switch (attr)
{
case R.styleable.CircleProgressView_base_progress_light_color:
base_progress_light_color = typedArray.getColor(attr, Color.WHITE);
break;
case R.styleable.CircleProgressView_progress_radius:
//轉(zhuǎn)化為px,TypedValue也可以將DIP(DP)轉(zhuǎn)PX
radius = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP
, 0, getResources().getDisplayMetrics()));
break;
case R.styleable.CircleProgressView_progress_clockwise:
progress_clockwise = typedArray.getBoolean(attr, true);
break;
case R.styleable.CircleProgressView_progress_period:
period = typedArray.getInteger(attr, 3000);
break;
......
}
}
}
- 最后姚垃,要一些順時針,逆時針盼忌,開始积糯,暫停等,就按照自己的需求谦纱,加入邏輯就好了看成。下載源碼可以看到更多具體細節(jié)。