项目中有一个圆形进度条的效果,写下实现过程做记录
- 效果图
首先创建一个CircleProgress.javapublic class CircleProgress extends View { public CircleProgressView(Context context) { super(context); } public CircleProgressView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); } public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) public CircleProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); } }进度条需要多个属性,res/value下创建一个attrs.xml文件,放View需要的自定义属性,attrs.xml内容
在第二个构造函数中,读取需要的配置项public CircleProgress(Context context, @Nullable AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleProgress); progressColor = ta.getColor(R.styleable.CircleProgress_progress_color, Color.parseColor("#00E091")); innerColor = ta.getColor(R.styleable.CircleProgress_inner_color, Color.parseColor("#00E091")); innerRadius = ta.getDimension(R.styleable.CircleProgress_inner_radius, dp2px(75)); ringColor = ta.getColor(R.styleable.CircleProgress_ring_color, Color.parseColor("#1A00E091")); ringWidth = ta.getDimension(R.styleable.CircleProgress_ring_width, dp2px(10)); ringRadius = ta.getDimension(R.styleable.CircleProgress_ring_radius, dp2px(100)); text = ta.getString(R.styleable.CircleProgress_text); textSize = ta.getDimension(R.styleable.CircleProgress_textSize, dp2px(36)); textColor = ta.getColor(R.styleable.CircleProgress_textColor, Color.parseColor("#FFFFFF")); ta.recycle(); init(); }
获取View 的中心点坐标@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //view的宽和高,相对于父布局(用于确定圆心) int viewWidth = getMeasuredWidth(); int viewHeight = getMeasuredHeight(); mViewCenterX = viewWidth / 2; mViewCenterY = viewHeight / 2; mRectF = new RectF( mViewCenterX - ringRadius, mViewCenterY - ringRadius, mViewCenterX + ringRadius, mViewCenterY + ringRadius ); }初始化一些画笔,和默认颜色private void init() { //圆环渐变的颜色 color[0] = Color.parseColor("#00F2C4"); color[1] = Color.parseColor("#00E091"); progressPaint = new Paint(); progressPaint.setColor(progressColor); setAntialias(progressPaint); ringPaint = new Paint(); ringPaint.setColor(ringColor); setAntialias(ringPaint); innerPaint = new Paint(); //设置抗锯齿 setAntialias(innerPaint); textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setAntiAlias(true); }在onDraw方法中开始绘制View
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //设置线性渐变给画笔,没有在init()中初始化是应为会失效 LinearGradient linearGradient = new LinearGradient(mViewCenterX, mViewCenterY - innerRadius, mViewCenterX, mViewCenterY + innerRadius, color, null, Shader.TileMode.CLAMP); innerPaint.setShader(linearGradient); //绘制内部实心圆 canvas.drawCircle(mViewCenterX, mViewCenterY, innerRadius, innerPaint); //绘制进度显示文字 drawTextWithCenterPoint(canvas, mViewCenterX, mViewCenterY, text, textPaint); //绘制默认进度条圆环 drawNormalRing(canvas); //绘制颜色显示圆环 drawColorRing(canvas); } private void drawTextWithCenterPoint(Canvas canvas, int centerX, int centerY, String text, Paint paint) { if (TextUtils.isEmpty(text)) { return; } paint.setTextSize(textSize); paint.setColor(textColor); //获取文本的宽度,但是是一个比较粗略的结果 float textWidth = paint.measureText(text); //文字度量 Paint.FontMetrics fontMetrics = paint.getFontMetrics(); //得到基线的位置 float baselineY = centerY + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; //绘制 canvas.drawText(text, centerX - textWidth / 2, baselineY, paint); } private void drawNormalRing(Canvas canvas) { Paint ringNormalPaint = new Paint(ringPaint); ringNormalPaint.setStyle(Paint.Style.STROKE); ringNormalPaint.setStrokeWidth(ringWidth); ringNormalPaint.setColor(ringColor);//圆环默认颜色为灰色 canvas.drawArc(mRectF, 0, 360, false, ringNormalPaint); } private void drawColorRing(Canvas canvas) { progressPaint.setStyle(Paint.Style.STROKE); progressPaint.setStrokeCap(Paint.Cap.ROUND);//使圆弧两头圆滑 progressPaint.setStrokeWidth(ringWidth); //加上渐变色 // progressPaint.setShader(new SweepGradient(mViewCenterX, mViewCenterX, color, null)); //逆时针旋转90度 canvas.rotate(180, mViewCenterX, mViewCenterY); canvas.drawArc(mRectF, startAngle, mSelectRing, false, progressPaint); // progressPaint.setShader(null); }用到的dp转px方法public int dp2px(float dpValue) { final float scale = Resources.getSystem().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); }
以上就是大概的过程,完整代码package com.cash.cashlibrary.widget; import android.animation.ValueAnimator; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.Resources; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.LinearGradient; import android.graphics.Paint; import android.graphics.RectF; import android.graphics.Shader; import android.text.TextUtils; import android.util.AttributeSet; import android.view.View; import androidx.annotation.Nullable; import com.cash.cashlibrary.R; public class CircleProgress extends View { private static final String TAG = "CircleProgress"; private Paint progressPaint; private int progressColor;//进度的颜色 //圆环相关 private Paint ringPaint; private int ringColor;//圆环的颜色 private float ringRadius; //圆环半径 private float ringWidth; //圆环的宽度 //内圆相关 private Paint innerPaint; private float innerRadius;//内圆的半径 private int innerColor; //内圆的颜色 //文字 private Paint textPaint; private float textSize;//文字大小 private int textColor; //文字颜色 private String text; //文字 private int progress = 0; //进度 private int maxValue = 100;//最大进度 public CircleProgress(Context context) { super(context); init(); } public CircleProgress(Context context, @Nullable AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.CircleProgress); progressColor = ta.getColor(R.styleable.CircleProgress_progress_color, Color.parseColor("#00E091")); innerColor = ta.getColor(R.styleable.CircleProgress_inner_color, Color.parseColor("#00E091")); innerRadius = ta.getDimension(R.styleable.CircleProgress_inner_radius, dp2px(75)); ringColor = ta.getColor(R.styleable.CircleProgress_ring_color, Color.parseColor("#1A00E091")); ringWidth = ta.getDimension(R.styleable.CircleProgress_ring_width, dp2px(10)); ringRadius = ta.getDimension(R.styleable.CircleProgress_ring_radius, dp2px(100)); text = ta.getString(R.styleable.CircleProgress_text); textSize = ta.getDimension(R.styleable.CircleProgress_textSize, dp2px(36)); textColor = ta.getColor(R.styleable.CircleProgress_textColor, Color.parseColor("#FFFFFF")); ta.recycle(); init(); } public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public CircleProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); init(); } private void drawNormalRing(Canvas canvas) { Paint ringNormalPaint = new Paint(ringPaint); ringNormalPaint.setStyle(Paint.Style.STROKE); ringNormalPaint.setStrokeWidth(ringWidth); ringNormalPaint.setColor(ringColor);//圆环默认颜色为灰色 canvas.drawArc(mRectF, 0, 360, false, ringNormalPaint); } public void setValue(int value) { if (value > maxValue) { value = maxValue; } int start = 0; int end = value; startAnimator(start, end, 1000); } private int mSelectRing = 90; //要显示的彩色区域(岁数值变化) private ValueAnimator valueAnimator; private void startAnimator(int start, int end, long animTime) { valueAnimator = ValueAnimator.ofInt(start, end); valueAnimator.setDuration(animTime); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { int i = Integer.parseInt(String.valueOf(animation.getAnimatedValue())); text = (i + " %"); //每个单位长度占多少度 mSelectRing = (int) (360 * (i / 100f)); invalidate(); } }); valueAnimator.start(); } private int color[] = new int[2]; //渐变颜色 private void init() { //圆环渐变的颜色 color[0] = Color.parseColor("#00F2C4"); color[1] = Color.parseColor("#00E091"); progressPaint = new Paint(); progressPaint.setColor(progressColor); setAntialias(progressPaint); ringPaint = new Paint(); ringPaint.setColor(ringColor); setAntialias(ringPaint); innerPaint = new Paint(); // innerPaint.setColor(innerColor); setAntialias(innerPaint); textPaint = new Paint(Paint.ANTI_ALIAS_FLAG); textPaint.setAntiAlias(true); } //防止边缘锯齿 private void setAntialias(Paint paint) { paint.setAntiAlias(true); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); LinearGradient linearGradient = new LinearGradient(mViewCenterX, mViewCenterY - innerRadius, mViewCenterX, mViewCenterY + innerRadius, color, null, Shader.TileMode.CLAMP); innerPaint.setShader(linearGradient); canvas.drawCircle(mViewCenterX, mViewCenterY, innerRadius, innerPaint); drawTextWithCenterPoint(canvas, mViewCenterX, mViewCenterY, text, textPaint); drawNormalRing(canvas); drawColorRing(canvas); } private void drawColorRing(Canvas canvas) { progressPaint.setStyle(Paint.Style.STROKE); progressPaint.setStrokeCap(Paint.Cap.ROUND);//使圆弧两头圆滑 progressPaint.setStrokeWidth(ringWidth); //加上渐变色 // progressPaint.setShader(new SweepGradient(mViewCenterX, mViewCenterX, color, null)); //逆时针旋转90度 canvas.rotate(180, mViewCenterX, mViewCenterY); canvas.drawArc(mRectF, startAngle, mSelectRing, false, progressPaint); // progressPaint.setShader(null); } private float startAngle = 0; private void drawTextWithCenterPoint(Canvas canvas, int centerX, int centerY, String text, Paint paint) { if (TextUtils.isEmpty(text)) { return; } paint.setTextSize(textSize); paint.setColor(textColor); //获取文本的宽度,但是是一个比较粗略的结果 float textWidth = paint.measureText(text); //文字度量 Paint.FontMetrics fontMetrics = paint.getFontMetrics(); //得到基线的位置 float baselineY = centerY + (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom; //绘制 canvas.drawText(text, centerX - textWidth / 2, baselineY, paint); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); } private int mViewCenterX; //view宽的中心点(可以暂时理解为圆心) private int mViewCenterY; private RectF mRectF; //圆环的矩形区域 @SuppressLint("DrawAllocation") @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); //view的宽和高,相对于父布局(用于确定圆心) int viewWidth = getMeasuredWidth(); int viewHeight = getMeasuredHeight(); mViewCenterX = viewWidth / 2; mViewCenterY = viewHeight / 2; mRectF = new RectF( mViewCenterX - ringRadius, mViewCenterY - ringRadius, mViewCenterX + ringRadius, mViewCenterY + ringRadius ); } public int dp2px(float dpValue) { final float scale = Resources.getSystem().getDisplayMetrics().density; return (int) (dpValue * scale + 0.5f); } }参考
Android自定义View之画圆环(手把手教你如何一步步画圆环)_君羊-CSDN博客_android 自定义圆环感谢观看,如果有错误的地方,欢迎留言指正。



