Android

Android-仿MIUI手机管家分数值进度条

2020-10-06  本文已影响0人  h2coder

这个也是一个进度条,之前在MIUI9上的手机管家上看到,效果是一段弧度,中间一个百分比文字。

原理分析

后续补

完整代码

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!-- 圆形CD进度View -->
    <declare-styleable name="CircleProgressWithTextView">
        <!-- 外圆颜色 -->
        <attr name="cpt_circle_color" format="reference|color" />
        <!-- 圆弧颜色 -->
        <attr name="cpt_remain_circle_color" format="reference|color" />
        <!-- 文字颜色 -->
        <attr name="cpt_text_color" format="reference|color" />
        <!--当前进度值-->
        <attr name="cpt_progress" format="integer" />
        <!--进度的最大值-->
        <attr name="cpt_max" format="integer" />
        <!--圆弧轮廓的宽度-->
        <attr name="cpt_remain_circle_border_width" format="dimension" />
        <!-- 是否显示进度百分比文字 -->
        <attr name="cpt_text_enable" format="boolean" />
        <!-- 背景颜色 -->
        <attr name="cpt_bg_color" format="reference|color" />
    </declare-styleable>
</resources>
public class CircleProgressWithTextView extends View {
    /**
     * 默认值
     */
    /**
     * 默认的当前进度,默认为0
     */
    private static final int DEFAULT_PROGRESS = 0;
    /**
     * 默认的最大值,默认为100
     */
    private static final int DEFAULT_MAX = 100;
    /**
     * 默认进度圆弧颜色
     */
    private final int DEFAULT_CIRCLE_COLOR = Color.parseColor("#0AA4A2");
    /**
     * 圆弧颜色
     */
    private final int DEFAULT_REMAIN_CIRCLE_COLOR = Color.parseColor("#EFEFF0");
    /**
     * 默认背景颜色
     */
    private final int DEFAULT_BG_COLOR = Color.parseColor("#00000000");
    /**
     * 默认文字颜色
     */
    private final int DEFAULT_TEXT_COLOR = Color.parseColor("#0AA4A2");
    /**
     * 是否显示进度百分比文字
     */
    private static final boolean DEFAULT_TEXT_ENABLE = true;
    /**
     * 绘制相关
     */
    private RectF mRect;
    private Paint mCirclePaint;
    private Paint mTextPaint;
    private Paint mPercentPaint;
    /**
     * 画笔颜色
     */
    private int mCircleColor;
    private int mRemainCircleColor;
    private int mTextColor;
    private int mBgColor;
    /**
     * 圆弧宽度
     */
    private float mCircleBorderWidth;
    /**
     * View相关尺寸
     */
    private int mWidth;
    private int mHeight;
    /**
     * 外圆半径
     */
    private float mRadius;
    /**
     * 当前进度
     */
    private float mProgress;
    /**
     * 进度最大值
     */
    private int mMax;
    /**
     * 是否显示百分比文字
     */
    private boolean mEnableText;
    /**
     * 弧线的开始角度,默认是0,是水平的,我们要从上面开始画
     */
    private float mStartAngle = -90f;
    /**
     * 中心点X、Y坐标
     */
    private int mCenterX;
    private int mCenterY;
    /**
     * 进度条进度
     */
    private ValueAnimator mProgressAnimator;
    private OnProgressUpdateListener mProgressUpdateListener;

    public CircleProgressWithTextView(Context context) {
        super(context);
        init(null);
    }

    public CircleProgressWithTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(attrs);
    }

    public CircleProgressWithTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(attrs);
    }

    /**
     * 初始化自定义属性
     */
    private void initAttributeVar(AttributeSet attrs) {
        //默认圆弧宽度
        int defaultCircleBorderWidth = dip2px(getContext(), 5f);
        if (attrs != null) {
            TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.CircleProgressWithTextView);
            mProgress = array.getInt(R.styleable.CircleProgressWithTextView_cpt_progress, DEFAULT_PROGRESS);
            mMax = array.getInt(R.styleable.CircleProgressWithTextView_cpt_max, DEFAULT_MAX);
            //Xml设置的进度圆弧颜色
            mCircleColor = array.getColor(R.styleable.CircleProgressWithTextView_cpt_circle_color, DEFAULT_CIRCLE_COLOR);
            //Xml设置的圆弧颜色
            mRemainCircleColor = array.getColor(R.styleable.CircleProgressWithTextView_cpt_remain_circle_color, DEFAULT_REMAIN_CIRCLE_COLOR);
            //文字颜色
            mTextColor = array.getColor(R.styleable.CircleProgressWithTextView_cpt_text_color, DEFAULT_CIRCLE_COLOR);
            //读取设置的圆弧轮廓宽度,读取dimension
            mCircleBorderWidth = array.getDimensionPixelSize(R.styleable.CircleProgressWithTextView_cpt_remain_circle_border_width, defaultCircleBorderWidth);
            //是否显示进度百分比文字
            mEnableText = array.getBoolean(R.styleable.CircleProgressWithTextView_cpt_text_enable, DEFAULT_TEXT_ENABLE);
            //背景颜色
            mBgColor = array.getColor(R.styleable.CircleProgressWithTextView_cpt_bg_color, DEFAULT_BG_COLOR);
            array.recycle();
        } else {
            //没有在Xml中设置属性,使用默认属性
            mProgress = DEFAULT_PROGRESS;
            mMax = DEFAULT_MAX;
            mCircleColor = DEFAULT_CIRCLE_COLOR;
            mRemainCircleColor = DEFAULT_REMAIN_CIRCLE_COLOR;
            mTextColor = DEFAULT_TEXT_COLOR;
            mCircleBorderWidth = defaultCircleBorderWidth;
            mEnableText = DEFAULT_TEXT_ENABLE;
        }
    }

    private void init(AttributeSet attrs) {
        initAttributeVar(attrs);
        //外圆画笔
        mCirclePaint = new Paint();
        mCirclePaint.setColor(mCircleColor);
        mCirclePaint.setStrokeWidth(mCircleBorderWidth);
        mCirclePaint.setStyle(Paint.Style.STROKE);
        //设置笔触为圆角
        mCirclePaint.setStrokeCap(Paint.Cap.ROUND);
        mCirclePaint.setAntiAlias(true);
        //文字画笔
        mTextPaint = new Paint();
        mTextPaint.setColor(mTextColor);
        mTextPaint.setStrokeWidth(dip2px(getContext(), 1f));
        mTextPaint.setStyle(Paint.Style.FILL);
        mTextPaint.setTextSize(sp2px(getContext(), 17f));
        mTextPaint.setAntiAlias(true);
        //百分比画笔
        mPercentPaint = new Paint();
        mPercentPaint.setColor(mTextColor);
        mPercentPaint.setStrokeWidth(dip2px(getContext(), 1f));
        mPercentPaint.setStyle(Paint.Style.FILL);
        mPercentPaint.setTextSize(sp2px(getContext(), 13f));
        mPercentPaint.setAntiAlias(true);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        //控件的总宽高
        mWidth = w;
        mHeight = h;
        //取出padding值
        int paddingLeft = getPaddingLeft();
        int paddingRight = getPaddingRight();
        int paddingTop = getPaddingTop();
        int paddingBottom = getPaddingBottom();
        //绘制范围
        mRect = new RectF();
        mRect.left = (float) paddingLeft;
        mRect.top = (float) paddingTop;
        mRect.right = (float) mWidth - paddingRight;
        mRect.bottom = (float) mHeight - paddingBottom;
        //计算直径和半径
        float diameter = (Math.min(mWidth, mHeight)) - paddingLeft - paddingRight;
        mRadius = (float) ((diameter / 2) * 0.98);
        //计算圆心的坐标
        mCenterX = mWidth / 2;
        mCenterY = mHeight / 2;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        setMeasuredDimension(measureSpec(widthMeasureSpec), measureSpec(heightMeasureSpec));
    }

    private int measureSpec(int measureSpec) {
        int result;
        int mode = MeasureSpec.getMode(measureSpec);
        int size = MeasureSpec.getSize(measureSpec);
        //默认大小
        int defaultSize = dip2px(getContext(), 55f);
        //指定宽高则直接返回
        if (mode == MeasureSpec.EXACTLY) {
            result = size;
        } else if (mode == MeasureSpec.AT_MOST) {
            //wrap_content的情况
            result = Math.min(defaultSize, size);
        } else {//未指定,则使用默认的大小
            result = defaultSize;
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.scale(0.93f, 0.93f, mCenterX, mCenterY);
        drawBg(canvas);
        float curProgress = getProgress();
        //画圆弧
        drawCircle(canvas, curProgress);
        if (mEnableText) {
            //画文字
            String progressText = String.valueOf((int) curProgress);
            drawProgressText(canvas, progressText);
        }
    }

    /**
     * 画背景
     */
    private void drawBg(Canvas canvas) {
        canvas.drawColor(mBgColor);
    }

    /**
     * 画圆弧
     */
    private void drawCircle(Canvas canvas, float curProgress) {
        mCirclePaint.setColor(mRemainCircleColor);
        canvas.drawCircle(mCenterX, mCenterY, mRadius, mCirclePaint);
        //绘制当前进度的弧线
        mCirclePaint.setColor(mCircleColor);
        float angle = 360 * (curProgress * 1.0f / getMax());
        canvas.drawArc(mRect, mStartAngle, angle, false, mCirclePaint);
        //绘制剩下的度数的弧线
        //float remainAngle = 360f - angle;
        //mCirclePaint.setColor(mRemainCircleColor);
        //canvas.drawArc(mRect, mStartAngle + angle, remainAngle, false, mCirclePaint);
    }

    /**
     * 画进度文字
     */
    private void drawProgressText(Canvas canvas, String progressText) {
        Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
        float baseLine = -(fontMetrics.ascent + fontMetrics.descent) / 2;
        float textWidth = mTextPaint.measureText(progressText);
        float startX = mCenterX - (textWidth / 2);
        float endY = mCenterY + baseLine;
        canvas.drawText(progressText, startX, endY, mTextPaint);
        //画百分比
        String percentText = "%";
        //计算百分比文字的起始X坐标
        float percentStartX = 0;
        if (progressText.length() == 1) {
            percentStartX = mCenterX + dip2px(getContext(), 5f);
        } else if (progressText.length() == 2) {
            percentStartX = mCenterX + dip2px(getContext(), 10f);
        }
        //百分比文字Y坐标,中心点Y坐标,和数值文字的Y坐标一样
        float percentEndY = mCenterY + baseLine;
        canvas.drawText(percentText, percentStartX, percentEndY, mPercentPaint);
    }

    /**
     * 指定时间,开始进度
     *
     * @param preProgress 之前的进度
     * @param duration    执行时间
     */
    public void startProgressByTime(int preProgress, long duration) {
        if (mProgressAnimator == null) {
            mProgressAnimator = ValueAnimator.ofInt(preProgress, mMax);
        }
        mProgressAnimator.setInterpolator(new LinearInterpolator());
        mProgressAnimator.setDuration(duration);
        mProgressAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Integer cValue = (Integer) animation.getAnimatedValue();
                setProgress(cValue);
                if (mProgressUpdateListener != null) {
                    mProgressUpdateListener.onProgressUpdate(cValue);
                }
            }
        });
        mProgressAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                if (mProgressUpdateListener != null) {
                    mProgressUpdateListener.onStart();
                }
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                //结束,设置回0
                setProgress(0);
                if (mProgressUpdateListener != null) {
                    mProgressUpdateListener.onEnd();
                }
            }
        });
        mProgressAnimator.start();
    }

    /**
     * 进度更新监听
     */
    public interface OnProgressUpdateListener {
        void onStart();

        /**
         * 进度更新
         *
         * @param curProgress 当前进度
         */
        void onProgressUpdate(int curProgress);

        void onEnd();
    }

    public static class OnProgressUpdateAdapter implements OnProgressUpdateListener {

        @Override
        public void onStart() {
        }

        @Override
        public void onProgressUpdate(int curProgress) {
        }

        @Override
        public void onEnd() {
        }
    }

    public void setOnProgressUpdateListener(OnProgressUpdateListener progressUpdateListener) {
        mProgressUpdateListener = progressUpdateListener;
    }

    public float getProgress() {
        return mProgress;
    }

    public void setProgress(float mProgress) {
        this.mProgress = mProgress;
        postInvalidate();
    }

    public float getMax() {
        return mMax;
    }

    public void setMax(int max) {
        this.mMax = max;
        postInvalidate();
    }

    public static int dip2px(Context context, float dipValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (dipValue * scale + 0.5f);
    }

    public static int px2dp(Context context, float pxValue) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f);
    }

    private int sp2px(Context context, float spVal) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,
                spVal, context.getResources().getDisplayMetrics());
    }
}

示例代码

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.hzh.circle.progress.sample.MainActivity">

    <com.hzh.circle.progress.sample.widget.CircleProgressWithTextView
        android:id="@+id/circleProgress"
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:layout_centerInParent="true"
        android:paddingLeft="10dp"
        android:paddingTop="10dp"
        android:paddingRight="10dp"
        android:paddingBottom="10dp"
        app:cpt_circle_color="#0AA4A2"
        app:cpt_text_color="#0AA4A2"
        app:cpt_max="100"
        app:cpt_progress="30"
        app:cpt_remain_circle_border_width="3dp"
        app:cpt_remain_circle_color="#EFEFF0"
        app:cpt_text_enable="true" />
</RelativeLayout>
public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final CircleProgressWithTextView circleProgress = findViewById(R.id.circleProgress);

        //使用值动画,不断更新进度
        ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
        animator.setInterpolator(new LinearInterpolator());
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setRepeatMode(ValueAnimator.RESTART);
        animator.setDuration(3000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                Float cValue = (Float) animation.getAnimatedValue();
                circleProgress.setProgress(cValue);
            }
        });
        animator.start();
    }
}
上一篇下一篇

猜你喜欢

热点阅读