欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

自定义控件之带渐变色的圆形进度条

程序员文章站 2022-05-26 20:10:34
...

自定义控件之带渐变色的圆形进度条

七夕到了,祝大家七夕节快乐!
今天给大家讲解一个简单的进度条的自定义。首页呢,我们参考咪咕善跑APP的效果:
 

自定义控件之带渐变色的圆形进度条

. 分析

从上面的效果图看呢,进度条的渐变色主要是在三个颜色上进行渐变。那我们先定义好三个渐变的颜色:

1private int colorGreen = 0xff16FCD7;
2private int colorYellow = 0xffFECB55;
3private int colorRed = 0xffFF0054;

然后我们看到进度条的头是圆角的,所以我们会用到:

1circlePaint.setStrokeCap(Paint.Cap.ROUND);

显然整个进度的绘制就是在canvas上画弧的过程,只要我们进度的数据算出需要画的弧度然后配上动画效果基本上就能完成了。

. 实践

我们主要看画的逻辑和动画的逻辑就可以了。
先看下在画布上画弧和文字的过程:

 1    @Override
 2    protected void onDraw(Canvas canvas) {
 3        super.onDraw(canvas);
 4        mPaint.setStyle(Paint.Style.STROKE);
 5        mPaint.setStrokeWidth(mPaintWidth);
 6        mPaint.setStrokeCap(Paint.Cap.ROUND);
 7        mPaint.setColor(mBackgroundColor);
 8        circlePaint.setStyle(Paint.Style.STROKE);
 9        circlePaint.setStrokeWidth(mPaintWidth);
10        circlePaint.setStrokeCap(Paint.Cap.ROUND);
11        circleBackgroundPaint.setStyle(Paint.Style.STROKE);
12        circleBackgroundPaint.setStrokeWidth(mPaintWidth / 2.0f);
13        circleBackgroundPaint.setStrokeCap(Paint.Cap.ROUND);
14        circleBackgroundPaint.setShader(radialGradient);
15        mPaint.setColor(mProgressColor);
16        if (isOver) {
17            canvas.drawArc(rectF, -90, mBackgroundSweepAngle, false, circlePaint);
18            mPaint.setColor(mOverProgressColor);
19            canvas.drawArc(rectF, -90, 360, false, mPaint);
20        } else {
21            circlePaint.setShader(sweepGradient);
22            if (mProgressSweepAngle == 360) {
23                canvas.drawArc(rectF, -88, 355, false, circlePaint);
24            } else {
25                canvas.drawArc(rectF, -88, mProgressSweepAngle, false, circlePaint);
26            }
27        }
28
29        if (!isHideCenterText) {
30            // 画中间数字
31            mPaint.setColor(mTextColor);
32            mPaint.setTextSize(DisplayUtils.sp2px(34));
33            mPaint.setStyle(Paint.Style.FILL);
34            mPaint.setTextAlign(Paint.Align.CENTER);
35            Paint.FontMetrics fontMetrics = mPaint.getFontMetrics();
36            float bottom = fontMetrics.bottom;
37            int baseLineY = (int) (mHeight / 2 + mPaint.getTextSize() / 2 - bottom);
38            if (Long.valueOf(mNumber) == mTotalKcal
39                    && mTakeKcal > mTotalKcal) {
40                mNumber = String.valueOf(mTakeKcal);
41            }
42            canvas.drawText(mNumber, mWidth / 2 - offSet, baseLineY, mPaint);
43            mTextPaint.setColor(mTextColor);
44            mTextPaint.setTextSize(DisplayUtils.sp2px(11));
45            mTextPaint.setTextAlign(Paint.Align.CENTER);
46            mTextPaint.setStyle(Paint.Style.FILL);
47            int topBaseLineY = (int) (mHeight / 2 - mPaint.getTextSize() / 2 - DisplayUtils.dp2px(19));
48            if (isOver) {
49                canvas.drawText("多吃了/千卡", mWidth / 2 - offSet, topBaseLineY, mTextPaint);
50            } else {
51                canvas.drawText("还可以吃/千卡", mWidth / 2 - offSet, topBaseLineY, mTextPaint);
52            }
53            mTextPaint.setColor(mSmallTextColor);
54            mTextPaint.setTextSize(DisplayUtils.sp2px(9));
55            int bottomBaseLineY = (int) (mHeight / 2 + mPaint.getTextSize() / 2 + DisplayUtils.dp2px(25));
56            canvas.drawText("预算: " + mTotalKcal, mWidth / 2 - offSet, bottomBaseLineY, mTextPaint);
57        }
58    }

实现渐变效果的呢就是通过下面的API:

1sweepGradient = new SweepGradient(mWidth / 2, mHeight / 2, colors, positions);
2circlePaint.setShader(sweepGradient);

然后我们用属性动画来做动画效果:

 1/**
 2     * @param current  消耗的卡路里所占的比例(消耗的/总的预算 * 360)
 3     *               或者
 4     *               超出的卡路里
 5     * @param isOver 是否超出预算
 6     */
 7    private void setAnimation(int current, boolean isOver) {
 8        this.isOver = isOver;
 9        ValueAnimator progressAnimator = ValueAnimator.ofInt(0, current);
10        progressAnimator.setDuration(2000);
11        progressAnimator.setTarget(mProgressSweepAngle);
12        progressAnimator.addUpdateListener(animation -> {
13            mProgressSweepAngle = ((int) animation.getAnimatedValue());
14            if (isOver) {
15                // 超出部分直接绘制进度条
16                mNumber = String.valueOf(mTotalKcal * mProgressSweepAngle / 360);
17                if (Integer.valueOf(mNumber) > mTakeKcal
18                        || mProgressSweepAngle == mProgressSweepAngleTotal) {
19                    mNumber = String.valueOf(mTakeKcal);
20                }
21            } else {
22                // 剩余部分作差绘制
23                mNumber = String.valueOf(mTotalKcal - mTotalKcal * mProgressSweepAngle / 360);
24                if (Integer.valueOf(mNumber) > (mTotalKcal - mTakeKcal)
25                        || mProgressSweepAngle == mProgressSweepAngleTotal) {
26                    mNumber = String.valueOf(mTotalKcal - mTakeKcal);
27                }
28            }
29            postInvalidate();
30        });
31        progressAnimator.start();
32    }

完整代码请微信搜索公众号:“南京Android部落” 后台私信邮箱地址获取哦~