Android自定义View之圆形进度条

项目中有一个圆形进度条的效果,写下实现过程做记录

  1. 效果图
    Android自定义View之圆形进度条_第1张图片
  2.  首先创建一个CircleProgress.java
    public 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);
        }
    }
  3. 进度条需要多个属性,res/value下创建一个attrs.xml文件,放View需要的自定义属性,attrs.xml内容
    
    
    
        
            
            
            
            
            
            
            
            
            
        
    
    
  4. 在第二个构造函数中,读取需要的配置项
    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();
        }

     
  5. 获取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
            );
        }
  6. 初始化一些画笔,和默认颜色
    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);
    
    
        }

  7. 在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);
        }
    
        /**
         * 以中心点绘制文字
         *
         * @param canvas
         * @param centerX
         * @param centerY
         * @param text
         * @param paint
         */
        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);
        }
         /**
         * 画默认圆环
         *
         * @param canvas
         */
        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);
        }
  8. 用到的dp转px方法
    public int dp2px(float dpValue) {
            final float scale = Resources.getSystem().getDisplayMetrics().density;
            return (int) (dpValue * scale + 0.5f);
    }

     
  9. 以上就是大概的过程,完整代码
    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();
        }
    
        /**
         * 画默认圆环
         *
         * @param canvas
         */
        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);
        }
    
    
        /**
         * 设置当前值
         *
         * @param value
         */
        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;
    
    
        /**
         * 以中心点绘制文字
         *
         * @param canvas
         * @param centerX
         * @param centerY
         * @param text
         * @param paint
         */
        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);
        }
    }
    

  10. 参考
    Android自定义View之画圆环(手把手教你如何一步步画圆环)_君羊-CSDN博客_android 自定义圆环
  11. 感谢观看,如果有错误的地方,欢迎留言指正。

你可能感兴趣的:(Android,android)