Android自定义计步器View刻度盘圆形进度view

本文介绍了一种自定义的Android圆形进度视图实现方法,该视图通过使用Canvas和Paint绘制圆环、文字和图标来展示用户的步数进度。支持动态设置目标步数和当前步数,并通过平滑动画实现进度更新。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

package com.tools.customviewdemo;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.DashPathEffect;
import android.graphics.Paint;
import android.graphics.PathEffect;
import android.graphics.Typeface;
import android.support.annotation.Nullable;
import android.support.v4.content.ContextCompat;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;

/**
 *
 * @author wj
 * @date 2018/1/20
 * api用法:{@link{setStepNum()设置目标步数和当前步数,刻度线的个数必须为360的因数}}
 */
public class CircleScaleView extends View {

    private Context mContext;
    private float mViewWidth;
    private float mViewHeight;
    private float mCircleRadius;
    private int mScaleLineLength;
    private int mLineNum = 36;
    private int mGoalStep = 16000;
    private int mCurrentStep = 14628;
    private int mRedLineNum;
    private int mLastRedLineNum;
    private String mGoalText;
    private String mSteps;

    private Paint mPaint;
    private TextPaint mTextPaint;
    private int bottomColorWhite;
    private int upColorRed;
    private int goalColor;

    private PathEffect mEffect;
    private float mDashWidth;

    public CircleScaleView(Context context) {
        this(context, null);
    }

    public CircleScaleView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    private void init(Context context) {
        mContext = context;
        mGoalText = context.getResources().getString(R.string.goal_text);
        mSteps = context.getResources().getString(R.string.steps);
        bottomColorWhite = ContextCompat.getColor(context, R.color.bottomColorWhite);
        upColorRed = ContextCompat.getColor(context, R.color.upColorRed);
        goalColor = ContextCompat.getColor(context, R.color.goalColor);
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(DipUtils.dip2px(context, 2));
        mDashWidth = DipUtils.dip2px(context, 2);
        mEffect = new DashPathEffect(new float[]{mDashWidth, mDashWidth * 4, mDashWidth, mDashWidth * 4}, 1);
        mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setTextAlign(Paint.Align.CENTER);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }

    public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        switch (specMode) {
            case MeasureSpec.UNSPECIFIED:
                result = size;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.EXACTLY:
                result = specSize;
                break;
        }
        return result;
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mViewWidth = getWidth();
        mViewHeight = getHeight();
        mCircleRadius = (float) (mViewWidth * 0.35);
        mScaleLineLength = (int) (mViewWidth * 0.06);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // draw bottom circle
        canvas.translate(mViewWidth / 2, mViewHeight / 2);
        mPaint.setColor(bottomColorWhite);
        mPaint.setPathEffect(mEffect);
        mPaint.setStrokeCap(Paint.Cap.SQUARE);
        mPaint.setStrokeWidth(DipUtils.dip2px(mContext, 2.5f));
        canvas.drawCircle(0, 0, mCircleRadius, mPaint);
        // draw text
        mTextPaint.setTextSize(mCircleRadius / 6);
        mTextPaint.setTypeface(Typeface.MONOSPACE);
        mTextPaint.setColor(goalColor);
        canvas.drawText(mGoalText + mGoalStep, 0, -mCircleRadius / 2, mTextPaint);
        mTextPaint.setTextSize((float) (mCircleRadius / 2.3));
        mTextPaint.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
        mTextPaint.setColor(bottomColorWhite);
        canvas.drawText(mCurrentStep + "", 0, DipUtils.dip2px(mContext, 10), mTextPaint);
        mTextPaint.setTextSize(mCircleRadius / 5);
        canvas.drawText(mSteps, DipUtils.dip2px(mContext, 10), mCircleRadius / 2, mTextPaint);
        // draw bitmap
        canvas.drawBitmap(BitmapFactory.decodeResource(getResources(), R.mipmap.icon_step),
                DipUtils.dip2px(mContext, 5) -mCircleRadius / 2,
                mCircleRadius / 2 - DipUtils.dip2px(mContext, 20), mPaint);
        // draw scale line
        mPaint.setPathEffect(null);
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        mPaint.setStrokeWidth(DipUtils.dip2px(mContext, 5));
        mPaint.setColor(bottomColorWhite);
        for (int i = 0; i < mLineNum; i++) {
            canvas.drawLine(0, -(mCircleRadius + mScaleLineLength + DipUtils.dip2px(mContext, 10)),
                    0, -(mCircleRadius + DipUtils.dip2px(mContext, 10)), mPaint);
            canvas.rotate(360 / mLineNum);
        }
        // draw red line
        mPaint.setColor(upColorRed);
        for (int i = 0; i < mRedLineNum; i++) {
            canvas.drawLine(0, -(mCircleRadius + mScaleLineLength + DipUtils.dip2px(mContext, 10)),
                    0, -(mCircleRadius + DipUtils.dip2px(mContext, 10)), mPaint);
            canvas.rotate(360 / mLineNum);
        }
    }

    public void setStepNum(int goalStep, int currentStep) {
        mGoalStep = goalStep;
        mCurrentStep = currentStep;
        final int redLineNum = mLineNum * currentStep / goalStep;
        ValueAnimator animator = ValueAnimator.ofInt(mLastRedLineNum, redLineNum);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                if ((int)(animation.getAnimatedValue()) != mRedLineNum ||
                        (int)(animation.getAnimatedValue()) == redLineNum) {
                    mRedLineNum = (int) animation.getAnimatedValue();
                    invalidate();
                }
            }
        });
        animator.setDuration(Math.abs((redLineNum - mLastRedLineNum) * 100));
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.start();
        mLastRedLineNum = redLineNum;
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值