Android 绘制圆形进度条
最近项目上有一些需求,需要绘制圆形的进度条满足设计上和交互上的需求:
实现思路
在画布上直接绘制View,需要了解一下几点
1.需要画一个圆
2.圆圈上有不同进度的颜色
3.圆圈中有进度数字的展示
4.圆圈中间还有可以自定义不同文案提示
一、画圆
需要使用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(0xe9, 0xe9, 0xe9));canvas.drawColor(Color.TRANSPARENT);mPaint.setStrokeWidth(mCircleLineStrokeWidth);mPaint.setStyle(Style.STROKE);// 位置mRectF.left = mCircleLineStrokeWidth / 2; // 左上角xmRectF.top = mCircleLineStrokeWidth / 2; // 左上角ymRectF.right = width - mCircleLineStrokeWidth / 2; // 左下角xmRectF.bottom = height - mCircleLineStrokeWidth / 2; // 右下角y// 绘制圆圈,进度条背景canvas.drawArc(mRectF, -90, 360, false, mPaint);
此时画出了默认的背景圆圈:
二、画进度圆弧
其实实现很简单,换另外一种颜色同样在画布上画出即可,支持此时画的不是360°,而是通过进度计算出来的一个圆弧。
mPaint.setColor(Color.rgb(0xf8, 0x60, 0x30));
canvas.drawArc(mRectF, -90, ((float) mProgress / mMaxProgress) * 360, false, mPaint);
如上即可,此时mProgress / mMaxProgress = 80/100;即可绘制出如下效果
三、画中间进度百分比
其实要用到Canvas中绘制文本的方法:
public void drawText(@NonNull String text, float x, float y, @NonNull Paint paint) {native_drawText(mNativeCanvasWrapper, text, 0, text.length(), x, y, paint.mBidiFlags,paint.mNativePaint, paint.mNativeTypeface);
}
如下:
// 绘制进度文案显示
mPaint.setStrokeWidth(mTxtStrokeWidth);
String text = mProgress + "%";
int textHeight = height / 4;
mPaint.setTextSize(textHeight);
int textWidth = (int) mPaint.measureText(text, 0, text.length());
mPaint.setStyle(Style.FILL);
canvas.drawText(text, width / 2 - textWidth / 2, height / 2 + textHeight / 2, mPaint);
主要要计算好画的位置,drawText中对应就是相关的位置,相当于居中显示进度百分比文案。效果如下:
四、画圆圈中间提示文案
方法同三,只不过要计算出画的位置即可,效果图如下:
五、总结
其实很多自定义的View都可以用Canvas直接画出来,如果项目上有类似这样的需求,可以先研究Canvas的使用和原理。
最后附上源码:
public class CircleProgressView extends View {private static final String TAG = "CircleProgressBar";private int mMaxProgress = 100;private int mProgress = 30;private final int mCircleLineStrokeWidth = 8;private final int mTxtStrokeWidth = 2;// 画圆所在的距形区域private final RectF mRectF;private final Paint mPaint;private final Context mContext;private String mTxtHint1;private String mTxtHint2;public CircleProgressView(Context context, AttributeSet attrs) {super(context, attrs);mContext = context;mRectF = new RectF();mPaint = new Paint();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);int width = this.getWidth();int height = this.getHeight();if (width != height) {int min = Math.min(width, height);width = min;height = min;}// 设置画笔相关属性mPaint.setAntiAlias(true);mPaint.setColor(Color.rgb(0xe9, 0xe9, 0xe9));canvas.drawColor(Color.TRANSPARENT);mPaint.setStrokeWidth(mCircleLineStrokeWidth);mPaint.setStyle(Style.STROKE);// 位置mRectF.left = mCircleLineStrokeWidth / 2; // 左上角xmRectF.top = mCircleLineStrokeWidth / 2; // 左上角ymRectF.right = width - mCircleLineStrokeWidth / 2; // 左下角xmRectF.bottom = height - mCircleLineStrokeWidth / 2; // 右下角y// 绘制圆圈,进度条背景canvas.drawArc(mRectF, -90, 360, false, mPaint);mPaint.setColor(Color.rgb(0xf8, 0x60, 0x30));canvas.drawArc(mRectF, -90, ((float) mProgress / mMaxProgress) * 360, false, mPaint);// 绘制进度文案显示mPaint.setStrokeWidth(mTxtStrokeWidth);String text = mProgress + "%";int textHeight = height / 4;mPaint.setTextSize(textHeight);int textWidth = (int) mPaint.measureText(text, 0, text.length());mPaint.setStyle(Style.FILL);canvas.drawText(text, width / 2 - textWidth / 2, height / 2 + textHeight / 2, mPaint);if (!TextUtils.isEmpty(mTxtHint1)) {mPaint.setStrokeWidth(mTxtStrokeWidth);text = mTxtHint1;textHeight = height / 8;mPaint.setTextSize(textHeight);mPaint.setColor(Color.rgb(0x99, 0x99, 0x99));textWidth = (int) mPaint.measureText(text, 0, text.length());mPaint.setStyle(Style.FILL);canvas.drawText(text, width / 2 - textWidth / 2, height / 4 + textHeight / 2, mPaint);}if (!TextUtils.isEmpty(mTxtHint2)) {mPaint.setStrokeWidth(mTxtStrokeWidth);text = mTxtHint2;textHeight = height / 8;mPaint.setTextSize(textHeight);textWidth = (int) mPaint.measureText(text, 0, text.length());mPaint.setStyle(Style.FILL);canvas.drawText(text, width / 2 - textWidth / 2, 3 * height / 4 + textHeight / 2, mPaint);}}public int getMaxProgress() {return mMaxProgress;}public void setMaxProgress(int maxProgress) {this.mMaxProgress = maxProgress;}public void setProgress(int progress) {this.mProgress = progress;this.invalidate();}public void setProgressNotInUiThread(int progress) {this.mProgress = progress;this.postInvalidate();}public String getmTxtHint1() {return mTxtHint1;}public void setmTxtHint1(String mTxtHint1) {this.mTxtHint1 = mTxtHint1;}public String getmTxtHint2() {return mTxtHint2;}public void setmTxtHint2(String mTxtHint2) {this.mTxtHint2 = mTxtHint2;}
}
Xml中配置:
<com.jackshy.demo.view.CircleProgressBarandroid:id="@+id/circleProgressbar"android:layout_width="74dp"android:layout_height="74dp"android:layout_centerInParent="true" />
MainActivity中使用:
import android.app.Activity;
import android.os.Bundle;
import android.view.Menu;import com.jackshy.demo.view.CircleProgressBar;public class MainActivity extends Activity {private CircleProgressBar mCircleBar;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initViews();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}private void initViews() {mCircleBar = (CircleProgressBar) findViewById(R.id.circleProgressbar);mCircleBar.setProgress(80);}}
上述还可以做成进度条的形式,这时候需要启动非UI线程调用此方法即可:
public void setProgressNotInUiThread(int progress) {this.mProgress = progress;this.postInvalidate();}
待续。