效果图
实现思路
- 在画布上直接绘制View,需要了解一下几点
- 1.需要画一个底层小圆和上层大圆
- 2.圆圈上有不同进度的颜色
- 3.颜色的变化规律是先慢慢变多再慢慢减少
### 一、画圆
需要使用Canvas的该方法
public void drawArc(@NonNull RectF oval, float startAngle, float sweepAngle, boolean useCenter, @NonNull Paint paint) { drawArc(oval.left, oval.top, oval.right, oval.bottom, startAngle, sweepAngle, useCenter, paint); }
如下画出了默认的背景圆圈:
// 设置画笔相关属性 mPaint.setAntiAlias(true); //抗锯齿 mPaint.setColor(Color.rgb(0x3a, 0x58, 0x5f)); canvas.drawColor(Color.TRANSPARENT); //设置背景透明 mPaint.setStrokeWidth(mCircleLineStrokeWidthBottom); mPaint.setStyle(Paint.Style.STROKE); // 位置 mRectF.left = mCircleLineStrokeWidth / 2; // 左上角x mRectF.top = mCircleLineStrokeWidth / 2; // 左上角y mRectF.right = width - mCircleLineStrokeWidth / 2; // 左下角x mRectF.bottom = height - mCircleLineStrokeWidth / 2; // 右下角y // 绘制圆圈,进度条背景 canvas.drawArc(mRectF, -90, 360, false, mPaint); //顺时针为正,起始点-90是视图上最高的点
二、画进度圆弧
其实实现很简单,换另外一种颜色同样在画布上画出即可,支持此时画的不是360°,而是通过进度计算出来的一个圆弧。
//绘制上面可变的进度条 mPaint.setColor(Color.rgb(0x2f, 0xc2, 0xe6)); mPaint.setStrokeWidth(mCircleLineStrokeWidthAbove); if (isOddNumber) { //奇数轮时 canvas.drawArc(mRectFAbove, -90, ((float) mProgress / mMaxProgress) * 360, false, mPaint); } else { //偶数轮时,反方向绘制 canvas.drawArc(mRectFAbove, -90, (-(float) mProgress / mMaxProgress) * 360, false, mPaint); }
三、定时器调用
设置定时器
private final int TIME_PROGRESS = 30; private int curProgress = 0; private boolean isOddNumber= false; //是否是奇数阶段 private Handler handler = new Handler(); Runnable runnableProgress = new Runnable() { @Override public void run() { if (circleProgressView != null) { if (curProgress == 0) { //定义奇数和偶数阶段 isOddNumber = true; } else if (curProgress >= 100) { isOddNumber = false; } if (isOddNumber) { //奇数阶段累加1 curProgress += 1; } else { //偶数阶段递减1 curProgress -= 1; } circleProgressView.setProgress(curProgress, isOddNumber); } handler.postDelayed(this, TIME_PROGRESS); //handler自带方法实现定时器 } };
启动
handler.postDelayed(runnableProgress, TIME_PROGRESS);//设置转圈进度定时器
关闭
handler.removeCallbacks(runnableProgress); //取消转圈进度定时器线程
四、总结
- 其实很多自定义的View都可以用Canvas直接画出来,只要研究透了Canvas的使用和原理。
五、最后附上源码
- 源码下载地址:https://github.com/yygmind/AndroidStudy
CircleProgressView
public class CircleProgressView extends View { private static final String TAG = "CircleProgressView"; private int mMaxProgress = 100; private int mProgress = 30; private final int mCircleLineStrokeWidthBottom = 10; private final int mCircleLineStrokeWidthAbove = 20; // 画圆所在的距形区域 private final RectF mRectFBottom; private final RectF mRectFAbove; private final Paint mPaint; private final Context mContext; private int width; private int height; private boolean isOddNumber= false; //是否是奇数阶段 public CircleProgressView(Context context, AttributeSet attrs) { super(context, attrs); mContext = context; mRectFBottom = new RectF(); mRectFAbove = new RectF(); mPaint = new Paint(); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); width = this.getWidth(); height = this.getHeight(); if (width != height) { int min = Math.min(width, height); width = min; height = min; } // 设置背景画笔相关属性 mPaint.setAntiAlias(true); //抗锯齿 mPaint.setColor(Color.rgb(0x3a, 0x58, 0x5f)); canvas.drawColor(Color.TRANSPARENT); //设置背景透明 mPaint.setStrokeWidth(mCircleLineStrokeWidthBottom); mPaint.setStyle(Paint.Style.STROKE); // 位置 setRectFPosition(mRectFBottom, mCircleLineStrokeWidthBottom); setRectFPosition(mRectFAbove, mCircleLineStrokeWidthAbove); // 绘制圆圈,进度条背景 canvas.drawArc(mRectFBottom, -90, 360, false, mPaint); //顺时针为正,起始点-90是视图上最高的点 //绘制上面可变的进度条 mPaint.setColor(Color.rgb(0x2f, 0xc2, 0xe6)); mPaint.setStrokeWidth(mCircleLineStrokeWidthAbove); if (isOddNumber) { //奇数轮时 canvas.drawArc(mRectFAbove, -90, ((float) mProgress / mMaxProgress) * 360, false, mPaint); } else { //偶数轮时,反方向绘制 canvas.drawArc(mRectFAbove, -90, (-(float) mProgress / mMaxProgress) * 360, false, mPaint); } } /** * 设置RectF位置 * @param mRectF * @param mCircleLineStrokeWidth */ private void setRectFPosition(RectF mRectF, int mCircleLineStrokeWidth) { mRectF.left = mCircleLineStrokeWidth / 2; // 左上角x mRectF.top = mCircleLineStrokeWidth / 2; // 左上角y mRectF.right = width - mCircleLineStrokeWidth / 2; // 左下角x mRectF.bottom = height - mCircleLineStrokeWidth / 2; // 右下角y } public int getMaxProgress() { return mMaxProgress; } public void setMaxProgress(int maxProgress) { this.mMaxProgress = maxProgress; } public void setProgress(int progress, boolean isOddNumber) { this.mProgress = progress; this.isOddNumber = isOddNumber; this.invalidate(); } public void setProgressNotInUiThread(int progress) { this.mProgress = progress; this.postInvalidate(); } }
Xml中配置:
<com.cody.myandroidstudy.view.CircleProgressView android:id="@+id/circle_progress_view" android:layout_width="200dp" android:layout_height="200dp" android:layout_centerInParent="true"/>
Activity中使用:
public class CircleProgressActivity extends BaseActivity { private final int TIME_PROGRESS = 30; private int curProgress = 0; private boolean isOddNumber= false; //是否是奇数阶段 private Handler handler = new Handler(); Runnable runnableProgress = new Runnable() { @Override public void run() { if (circleProgressView != null) { if (curProgress == 0) { //定义奇数和偶数阶段 isOddNumber = true; } else if (curProgress >= 100) { isOddNumber = false; } if (isOddNumber) { //奇数阶段累加1 curProgress += 1; } else { //偶数阶段递减1 curProgress -= 1; } circleProgressView.setProgress(curProgress, isOddNumber); } handler.postDelayed(this, TIME_PROGRESS); //handler自带方法实现定时器 } }; private CircleProgressView circleProgressView;
@Override
public void initView() {
setContentView(R.layout.activity_circle_progress);
circleProgressView = (CircleProgressView) findViewById(R.id.circle_progress_view);
}
@Override
public void initData() {
}
@Override
public void initListener() {
}
@Override
public void progress(View v) {
}
@Override
protected void onStart() {
super.onStart();
handler.postDelayed(runnableProgress, TIME_PROGRESS);//设置转圈进度定时器
}
@Override
protected void onStop() {
super.onStop();
handler.removeCallbacks(runnableProgress); //取消转圈进度定时器线程
}
}