在学习本篇之前,你首先需要了解如下知识点:
自定义属性
自定义view中常用的绘图函数
额外补充知识点
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
MeasureSpec对象包含了测量的模式和大小。他是一个32位的int值,其中高两位为测量的模式,低30位是测量的大小。采用位运算和运行效率有关。所以可以从一个MeasureSpec对象分别获取模式和值 如:
//获取模式 值为 EXACTLY AT_MOST UNSPECIFIED
int specMode = MeasureSpec.getMode(measureSpec);
//获取测量值
int specSize = MeasureSpec.getSize(measureSpec);
好了,正式开始,效果图如下:
来分析一下,其实总共分为三层
- 最底层是一个大圆形,作为底色
- 第二层是一个扇形,大小和最底层的大圆形一样,用来表示进度
- 第三层是一个小圆形,比最底层的大原形小,用来实现弧形效果
实现步骤如下:
-
我们先画一个大原形
-
再绘制一个扇形
-
再绘制小圆形
分析完成后开始正式编码,首先去创建一个CirclePrecentView继承自view,重写构造方法,加载出自定义属性的值
public class CirclePrecentView extends View {
private int mWidth;//宽度
private int mHeight;//高度
private int mBigCircleColor; //背景颜色
private int mStripeColor;//弧带颜色
private int mRadius;//半径
private int mCenterX;//圆心x轴
private int mCenterY;//圆心y轴
private int mStripeWidth;//弧带宽度
private int mPrecent;//百分比
public CirclePrecentView(Context context) {
this(context, null, 0);
}
public CirclePrecentView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
private void initViariable(Context context, AttributeSet attrs) {
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CirclePrecentView);
mRadius = (int) typedArray.getDimension(R.styleable.CirclePrecentView_radius, 200);
mStripeColor = typedArray.getColor(R.styleable.CirclePrecentView_stripeColor, 0xffafb4db);
mBigCircleColor = typedArray.getColor(R.styleable.CirclePrecentView_bigCircleColor, 0xff6950a1);
mPrecent = typedArray.getInteger(R.styleable.CirclePrecentView_precent, 50);
mStripeWidth = (int) typedArray.getDimension(R.styleable.CirclePrecentView_stripeWidth, 50);
}
public CirclePrecentView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initViariable(context, attrs);
}
自定义属性如下:
对于自定义控件,我们必须测量控件的实际高度,计算出大圆形的半径,圆心位置等
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
if (heightMode == MeasureSpec.EXACTLY && widthMode == MeasureSpec.EXACTLY) {
mWidth = widthSize;
mHeight = heightSize;
mRadius = mWidth / 2;
}
if (heightMode == MeasureSpec.AT_MOST && widthMode == MeasureSpec.AT_MOST) {
mWidth = mRadius * 2;
mHeight = mRadius * 2;
}
mCenterX = mRadius;
mCenterY = mRadius;
setMeasuredDimension(mWidth, mHeight);
}
之后我们就可以开始绘制了,绘制步骤也是完全按照我们上面分析的。
@Override
protected void onDraw(Canvas canvas) {
int endAngle = (int) (mPrecent * 3.6);
//画背景
Paint bigCirclePaint = new Paint();
bigCirclePaint.setColor(mBigCircleColor);
bigCirclePaint.setAntiAlias(true);
canvas.drawCircle(mCenterX, mCenterY, mRadius, bigCirclePaint);
//画弧带
Paint stripePaint = new Paint();
stripePaint.setColor(mStripeColor);
stripePaint.setAntiAlias(true);
RectF rect = new RectF(0, 0, mWidth, mHeight);
canvas.drawArc(rect, 270, endAngle, true, stripePaint);
//画小圆
Paint smallCirclePaint = new Paint();
smallCirclePaint.setColor(mBigCircleColor);
smallCirclePaint.setAntiAlias(true);
canvas.drawCircle(mCenterX, mCenterY, mRadius - mStripeWidth, smallCirclePaint);
//画文字
Paint textPaint = new Paint();
textPaint.setTextSize(30);
String text = mPrecent + "%";
int textLength = (int) textPaint.measureText(text);
textPaint.setColor(Color.WHITE);
canvas.drawText(text, mCenterX - textLength / 2, mCenterY, textPaint);
}
到此呢,绘制圆形进度的工作就完成了,但是我们经常看到别人家的进度条都是动态的,要实现这个也简单
view.invalidate() 主线程运行,发送重绘消息
view.postinvalidate() 子线程运行,发送重回消息
使用上面的方法通知view进行重绘,就会重新执行onDraw()方法,我们只需要变化进度即可
public void setPrecentAnimator(final int precent) {
new Thread(new Runnable() {
@Override
public void run() {
for (int i = 0; i <= precent; i++) {
SystemClock.sleep(15);
mPrecent = i;
CirclePrecentView.this.postInvalidate();
}
}
}).start();
}
public void setmPrecent(int precent) {
mPrecent = precent;
this.invalidate();
}
代码下载
https://yunpan.cn/c6qi3hixXS5gE (提取码:3543)