代码下载链接点击此处
1.概述
接触Android有一段时间了,以前碰到自定义View感觉发憷,也看了很多大神的自定义View感觉很炫酷,由于自己水平有限,于是就从自定义View的基础开始自己学习,正好自己最近看到了华为应用市场的下载进度条,感觉不是很难,于是就开始项自己动手写一个,提升自己的自定义View水平;
先看一下效果图
思路
- 继承ProgressBar
- 测量
- 绘制
继承ProgressBar
/**
* Created by shion on 2016/12/17.
* 带按钮的水平进度条
*/
public class HorizontalDownloadProgressBar extends ProgressBar {
public static final int DOWNLOADING = 0x001;//下载
public static final int PAUSE = 0x002;//下载暂停
public static final int FINISH = 0x003;//下载完成
public static final int START = 0x004;//下载开始
private int mState = START;//默认状态时开始
private int mProgressTextColor = Color.parseColor("#4599E9");//进度条文字默认颜色
private int mProgressBarColor = Color.parseColor("#D4E9FA");//进度条默认背景颜色
private int mProgressTextSize = 18;//进度条中字体的大小
private int mProgressMinHeight = 20;//进度条最小高度
private int mProgressMinWidth = 30;//进度条最小宽度
private Paint mPaint;
private Path mPath;
public void setProgressState(int state) {
mState = state;
postInvalidate();
}
public int getProgressState() {
return mState;
}
public HorizontalDownloadProgressBar(Context context, AttributeSet attrs) {
super(context, attrs, 0);//此处要这样搞
obtainStyledAttributes(context, attrs);
initProgressBar();
}
//初始化xml中设置的自定义属性
private void obtainStyledAttributes(Context context, AttributeSet attrs) {
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.HorizontalDownloadProgressBar);
mProgressTextColor = ta.getColor(R.styleable.HorizontalDownloadProgressBar_progress_text_color, mProgressTextColor);
mProgressTextSize = (int) ta.getDimension(R.styleable.HorizontalDownloadProgressBar_progress_text_size, mProgressTextSize);
mProgressBarColor = ta.getColor(R.styleable.HorizontalDownloadProgressBar_progress_bar_color, mProgressBarColor);
mProgressMinHeight = (int) ta.getDimension(R.styleable.HorizontalDownloadProgressBar_progress_min_height, mProgressMinHeight);
mProgressMinWidth = (int) ta.getDimension(R.styleable.HorizontalDownloadProgressBar_progress_min_width, mProgressMinWidth);
ta.recycle();
}
private void initProgressBar() {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPath = new Path();
mPaint.setTextSize(sp2px(mProgressTextSize));
mPaint.setStrokeWidth(dp2px(1));
setClickable(true);//设置为可点击
}
private int dp2px(int dpValue) {
float pxValue = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());
return (int) (pxValue + 0.5f);
}
private int sp2px(int spValue) {
float pxValue = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spValue, getResources().getDisplayMetrics());
return (int) (pxValue + 0.5f);
}
2.测量onMeasure()
//此处主要测量文字的高度和ProgressBar的高度之间的最大值
@Override
protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int heightSize = measureHeight(heightMeasureSpec);//拿到高度
//比较一下文字的高度和height的大小,谁大用谁的
setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), heightMeasureSpec);
}
private int measureHeight(int heightSize) {
//高度这里默认再加上10dp的间距
int textHeight = (int) (Math.abs(mPaint.descent() - mPaint.ascent()) + dp2px(10));
//取得用户设置的最小最高、和文字的高度、和进度条自身的高度的三者的最大值 返回
return Math.max(textHeight, Math.min(heightSize, mProgressMinHeight));
}
这段代码主要是测量当View设置了自己的wrap_content的时候,控件的宽高该如何设置;
3.绘制onDraw()
//分别绘制四种状态
@Override
protected synchronized void onDraw(Canvas canvas) {
switch (mState) {
case DOWNLOADING:
drawProgressOnDownload(canvas);
break;
case PAUSE:
drawProgressOnPause(canvas);
break;
case FINISH:
drawProgressOnFinished(canvas);
break;
case START:
drawProgressOnStart(canvas);
break;
default:
break;
}
}
3.1绘制矩形框
// 绘制背景矩形
private void drawProgressRectBackground(Canvas canvas) {
int width = getWidth() - getPaddingRight() - getPaddingLeft();
int height = getHeight() - getPaddingBottom() - getPaddingTop();
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mProgressBarColor);
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
//绘制一个路径
mPath.reset();
float pw = mPaint.getStrokeWidth();//画笔的宽度
mPath.moveTo(height / 2.0f - pw / 2, height - pw / 2);//从这个点开始
mPath.arcTo(new RectF(pw / 2, pw / 2, height * 1.0f - pw / 2, height * 1.0f - pw / 2), 90, 180);//添加一个左边弧形
mPath.lineTo(width - height / 2.0f - pw / 2, pw / 2);//连接这个点
mPath.arcTo(new RectF(width - height + pw / 2, pw / 2, width - pw / 2, height - pw / 2), -90, 180);//添加一个右边弧形
mPath.lineTo(width - height / 2.0f - pw / 2, height - pw / 2);//连接这个点
mPath.lineTo(height / 2.0f - pw / 2, height - pw / 2);//连接这个点
mPath.close();//关闭
canvas.drawPath(mPath, mPaint);
canvas.restore();
}
3.2绘制下载进度
// 绘制矩形的下载进度
private void drawProgressRect(Canvas canvas) {
int width = getWidth() - getPaddingRight() - getPaddingLeft();//考虑左右边距
int height = getHeight() - getPaddingBottom() - getPaddingTop();//考虑上下边距
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setColor(mProgressBarColor);
float radio = getProgress() * 1.0f / getMax();
int progress = (int) (width * radio);
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
mPath.reset();
//绘制一个路径
mPath.moveTo(height / 2.0f, height);
mPath.arcTo(new RectF(0, 0, height * 1.0f, height * 1.0f), 90, 180);
mPath.lineTo(width - height / 2.0f, 0);
mPath.arcTo(new RectF(width - height, 0, width, height), -90, 180);
mPath.lineTo(width - height / 2.0f, height);
mPath.lineTo(height / 2.0f, height);
mPath.close();
//绘制进度
mPaint.setStyle(Paint.Style.FILL);
mPaint.setColor(mProgressBarColor);
canvas.clipPath(mPath);
mPath.reset();
mPath.addRect(new RectF(0, 0, progress, height), Path.Direction.CCW);
canvas.clipPath(mPath, Region.Op.INTERSECT);
canvas.drawColor(mProgressBarColor);
canvas.restore();
}
3.3绘制下载中的文字
// 绘制进度中的文字
private void drawProgressText(Canvas canvas, String text) {
int width = getWidth() - getPaddingRight() - getPaddingLeft();
int height = getHeight() - getPaddingBottom() - getPaddingTop();
//绘制文字
mPaint.setStyle(Paint.Style.FILL);
canvas.save();
canvas.translate(getPaddingLeft(), getPaddingTop());
mPaint.setColor(mProgressTextColor);
int textWidth = (int) mPaint.measureText(text);
int textHeight = (int) (mPaint.descent() + mPaint.ascent());
canvas.drawText(text, width / 2.0f - textWidth / 2.0f, height / 2.0f - textHeight / 2.0f, mPaint);
canvas.restore();
}