前言

自定义View作为安卓开发进阶必备的一个技能,其中不乏一些套路,当然更多的是相关Api的熟练使用以及一些数学计算。学而时习之,多学习多模仿,希望能更好的掌握自定义View的使用。

先看下我们要实现的效果, 主要是进度条和文字的更新

progressView.gif

要巩固掌握的知识点

自定义view的步骤和流程

Paint画笔的使用,画圆弧,画文字,文字基线的确定

属性动画的使用

效果实现分析

自定义属性:轨道圆弧的宽度和颜色,进度圆弧的颜色,文字的大小和颜色

用画笔绘制轨道圆弧,指定起始角度和结束角度

用画笔绘制进度圆弧,注意其sweepAngle是用属性动画更新的一个变量实现动画效果

用画笔绘制内部文字,注意文字的y坐标是基线位置

提供设置最大进度和当前进度的方法给使用者调用

代码实现

定义自定义属性

初始化轨道、进度圆弧和文字画笔

//轨道画笔
mTrackPaint = new Paint();
mTrackPaint.setColor(mTrackColor);
mTrackPaint.setAntiAlias(true);
mTrackPaint.setStrokeWidth(mTrackWidth);
mTrackPaint.setStyle(Paint.Style.STROKE);
mTrackPaint.setStrokeCap(Paint.Cap.ROUND);
//进度条画笔
mProgressPaint = new Paint();
mProgressPaint.setColor(mProgressColor);
mProgressPaint.setAntiAlias(true);
mProgressPaint.setStrokeWidth(mTrackWidth);
mProgressPaint.setStyle(Paint.Style.STROKE);
mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
//文字画笔
mTextPaint = new Paint();
mTextPaint.setTextSize(mProgressTextSize);
mTextPaint.setColor(mProgressTextColor);
mTextPaint.setAntiAlias(true);

在onDraw()方法中绘制 轨道圆弧、进度条圆弧、进度文字

进度条圆弧sweepAngle的计算 = 轨道的圆弧的角度 * (当前更新的进度 / 总进度)

画文字时,y坐标是基线高度,如果直接给定控件高度的一半。则画出来的文字会偏上,这里要计算基线高度

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//控件中心位置
int center = getWidth() / 2;
//轨道圆弧半径
int radius = getWidth() / 2 - mTrackWidth / 2;
//画轨道圆弧
RectF rectF = new RectF(center - radius, center - radius, center + radius, center + radius);
canvas.drawArc(rectF, 135, 270, false, mTrackPaint);
//计算当前进度圆弧扫过的角度
float sweepAngle = (mCurrentProgress / mMaxProgress) * 270;
//画进度条圆弧
canvas.drawArc(rectF, 135, sweepAngle, false, mProgressPaint);
//画文字
String text = (int) mCurrentProgress + "";
Rect rect = new Rect();
mTextPaint.getTextBounds(text, 0, text.length(), rect);
Paint.FontMetricsInt fontMetrics = mTextPaint.getFontMetricsInt();
float x = center - rect.width() / 2;
//确定文字绘制的基线高度
float dy = (fontMetrics.bottom - fontMetrics.top)/2 - fontMetrics.bottom;
float baseline = getHeight() / 2 + dy;
canvas.drawText(text, 0, text.length(), x, baseline, mTextPaint);
}

提供设置当前进度和最大进度的方法

使用属性动画,使记录当前进度的局部变量mCurrentProgress的值从0变到设置的progress,开始动画。调用invalidate() 刷新,使界面不断重绘以达到动画的效果

/**
* 设置当前进度
* @param progress
*/
public void setCurrentProgress(float progress) {
//属性动画更新进度,刷新界面
ValueAnimator animator = ValueAnimator.ofFloat(0, progress);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
mCurrentProgress = (float) valueAnimator.getAnimatedValue();
invalidate();
}
});
animator.setInterpolator(new AccelerateInterpolator());
animator.setDuration(2000);
animator.start();
}
/**
* 设置最大进度
* @param maxProgress
*/
public void setMaxProgress(float maxProgress) {
this.mMaxProgress = maxProgress;
}

在Activity中测试和使用

ProgressView progressView = (ProgressView) findViewById(R.id.progressView);
progressView.setMaxProgress(4000);
progressView.setCurrentProgress(2855);

小结

这个效果,主要是使用的画笔绘制圆弧并配合属性动画更新进度的显示。以此类推,水平进度条、圆形进度条和App开屏页的倒计时跳过的效果同样可以实现。效果中需要重点掌握和理解的是文字的绘制以及文字基线的定义和计算。