很早之前项目中需要展示进度管理做的,今天总结一下。为后面更复杂的自定义view打下基础。
功能很简单,但是里面涉及了两个自定义控件很重要的两个方法 onMeasure 和 onDraw,用来确定控件的大小和内容
话不多说先看效果
![](https://img-blog.csdnimg.cn/img_convert/fe6c2643e38735bb0dbc34fdef0f4694.gif)
自定义view 大体分为三类:
直接继承View
继承ViewGroup
继承原有控件
根据项目需求选择最优的实现方式。
因为当前的view是一个圆形进度图所以它的宽高是固定并且相等的,重写onMeasure设置当前view的大小
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(Math.min(width,height),Math.min(width,height));
}
当前控件选择继承view来实现。可以看到当前view可以分三个部分 外圈弧、内圈弧、进度文字。根据这三个部分一步步去实现。
首先绘制外弧背景 角度为0到360度
//画背景弧
canvas.drawArc(left, top,right, bottom, 0,
360, false, backProgressPaint);
画内弧 -90为圆弧绘制的开始角度,因为内弧的角度和进度有关,所以我们可以定义一个变量sweepAngle 在变化时去进行重绘
//进度弧
canvas.drawArc(left, top,right, bottom, -90,
sweepAngle, false, progressPaint);
画进度文字,使用canvas绘制文字的时候需要去计算当前文字的基线才能使文字居中
Rect rect = new Rect();
textPaint.getTextBounds(mProgress,0,mProgress.length(),rect);
float dx = getWidth()/2-rect.width()/2;
// 获取基线
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
int dy = (int) ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
int baseLine = getHeight() / 2 + dy;
canvas.drawText(mProgress,dx, baseLine,textPaint);
绘制已经完成了,想要动起来需要添加数值动画,监听回调并调用invalidate方法进行重绘操作
private void startAnimator(){
//当前数值变化从0到1
valueAnimator = ValueAnimator.ofFloat(1);
valueAnimator.setDuration(4000);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animationValue = (float) animation.getAnimatedValue();
Log.d(TAG, "onAnimationUpdate: "+animationValue);
sweepAngle = (int) (360 * animationValue);
mProgress = (int) (animationValue * 100) + "%";
//进行重绘
invalidate();
}
});
valueAnimator.start();
}
完整代码如下:
public class CircularProgress extends View{
private static final String TAG = "CircularProgress";
// 文字画笔
Paint textPaint;
// 弧形背景画笔
Paint backProgressPaint;
// 弧形进度画笔
Paint progressPaint;
//进度
String mProgress = "0%";
int textColor = R.color.color_009ad6;
int backProgressColor = R.color.color_90d7ec;
int progressColor = R.color.color_009ad6;
//圆弧的宽度
float progressWidgt = UtilView.dip2px(20);
Context mContext;
int sweepAngle = 0;
ValueAnimator valueAnimator = null;
public CircularProgress(Context context) {
this(context,null);
}
public CircularProgress(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public CircularProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
textPaint = initPaint(textColor);
textPaint.setTextSize(UtilView.sp2px(18));
textPaint.setTypeface(Typeface.DEFAULT_BOLD);
backProgressPaint = initPaint(backProgressColor);
backProgressPaint.setStrokeWidth(progressWidgt);
backProgressPaint.setStyle(Paint.Style.STROKE);
backProgressPaint.setColor(Color.parseColor("#90d7ec"));
progressPaint = initPaint(progressColor);
progressPaint.setStrokeWidth(progressWidgt);
progressPaint.setStyle(Paint.Style.STROKE);
progressPaint.setColor(Color.parseColor("#009ad6"));
progressPaint.setStrokeCap(Paint.Cap.ROUND);
}
private Paint initPaint(int color) {
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(color);
return paint;
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
setMeasuredDimension(Math.min(width,height),Math.min(width,height));
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
float left = 0 + progressWidgt / 2;
float top = 0 + progressWidgt / 2;
float right = getWidth() - progressWidgt / 2;
float bottom = getHeight() - progressWidgt / 2;
//画背景弧
canvas.drawArc(left, top,right, bottom, 0,
360, false, backProgressPaint);
//进度弧
canvas.drawArc(left, top,right, bottom, -90,
sweepAngle, false, progressPaint);
Rect rect = new Rect();
textPaint.getTextBounds(mProgress,0,mProgress.length(),rect);
float dx = getWidth()/2-rect.width()/2;
// 获取基线
Paint.FontMetrics fontMetrics = textPaint.getFontMetrics();
int dy = (int) ((fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom);
int baseLine = getHeight() / 2 + dy;
canvas.drawText(mProgress,dx, baseLine,textPaint);
}
public void setStateProgress(){
startAnimator();
}
private void startAnimator(){
valueAnimator = ValueAnimator.ofFloat(1);
valueAnimator.setDuration(4000);
valueAnimator.setInterpolator(new DecelerateInterpolator());
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
float animationValue = (float) animation.getAnimatedValue();
Log.d(TAG, "onAnimationUpdate: "+animationValue);
sweepAngle = (int) (360 * animationValue);
mProgress = (int) (animationValue * 100) + "%";
invalidate();
}
});
valueAnimator.start();
}
public void stopAnimator(){
if(valueAnimator != null){
valueAnimator.cancel();
valueAnimator = null;
}
}
}