Android 自定义view 定制一个带比例的环形进度控件

自定义view 定制一个带比例的环形进度控件,效果图如下:
   

大致思路如下:

  1. 最外层的圆弧
  2. 内层显示进度的圆弧
  3. view内文字显示
 @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * 中心坐标
         */
        float mCenterX = getWidth() / 2;
        /**
         * 圆弧外轮廓 矩形区域
         */
        RectF mRectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2);

        //画背景圆弧
        drawArcOne(canvas, mRectF);
        //画进度圆弧
        drawArcTwo(canvas, mRectF);
        //画中间数字
        drawTextNum(canvas, mCenterX);
        //画中间文字
        drawText(canvas, mCenterX);

    }

最外层的圆弧,绘制开始:

/**
     * 绘制最外层圆弧
     * @param canvas 
     * @param mRectF 
     */
    private void drawArcOne(Canvas canvas, RectF mRectF) {

        Paint paint = new Paint();
        //画笔颜色
        paint.setColor(Color.parseColor("#e0f9ff"));
        //画笔结合处类型 圆形
        paint.setStrokeJoin(Paint.Join.ROUND);
        //护臂落笔出类型  圆形
        paint.setStrokeCap(Paint.Cap.ROUND);
        //画笔类型 设置为 仅填充边框 
        paint.setStyle(Paint.Style.STROKE);
        //设置抗锯齿
        paint.setAntiAlias(true);
        //设置边框宽度
        paint.setStrokeWidth(mBorderWidth);

        /**
         * mRectF 圆形外矩形
         * mStartAngle 起始角度
         * mEndAngle   结束角度
         */
        canvas.drawArc(mRectF, mStartAngle, mEndAngle, false, paint);

    }

绘制内层进度圆弧

/**
     * 绘制内层进度圆弧
     * @param canvas
     * @param mRectF
     */
    private void drawArcTwo(Canvas canvas, RectF mRectF) {

        Paint paint = new Paint();
        //画笔颜色
        paint.setColor(getResources().getColor(R.color.blue_light));
        //画笔宽度
        paint.setStrokeWidth(mBorderWidth);
        //画笔抗锯齿
        paint.setAntiAlias(true);
        //Paint.Style.STROKE 仅填充描边
        paint.setStyle(Paint.Style.STROKE);
        //画笔触笔类型 Paint.Cap.ROUND 圆形
        paint.setStrokeCap(Paint.Cap.ROUND);
        //结合处为 Paint.Join.ROUND 圆形
        paint.setStrokeJoin(Paint.Join.ROUND);

        canvas.drawArc(mRectF, mStartAngle, mCurrentAngle, false, paint);
    }

绘制内层数字

/**
     * 绘制内层数字
     * @param canvas
     * @param centerX
     */
    private void drawTextNum(Canvas canvas, float centerX) {
        Paint paint = new Paint();
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setAntiAlias(true);
        paint.setTextSize(mTextSize);
        paint.setColor(getResources().getColor(R.color.black));
        //字体类型
        Typeface mTypeFace = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
        paint.setTypeface(mTypeFace);
        Rect rect = new Rect();
        paint.getTextBounds(mStepNum, 0, mStepNum.length(), rect);
        //获取文字基线位置
        float biseY = getTextBaseline(paint, getHeight() / 2);
        //为了显示数字之下的文字,在基线坐标基础上减去文字高度的一半
        canvas.drawText(mStepNum, centerX, biseY - rect.height() / 2, paint);
    }

绘制数字下的文字


    /**
     * 绘制文字
     * @param canvas
     * @param mCenterX
     */
    private void drawText(Canvas canvas, float mCenterX) {
        Paint paint = new Paint();
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setColor(getResources().getColor(R.color.black));
        paint.setAntiAlias(true);
        paint.setTextSize(dipToPx(18));
        String s = "步数";
        Rect rect = new Rect();
        paint.getTextBounds(s, 0, 2, rect);
        float biseY = getTextBaseline(paint, getHeight() / 2);

        canvas.drawText(s, mCenterX, biseY + getFontHeight(mTextSize), paint);

    }

对外的方法

 /**
     * 对外的方法
     * @param mTotalNum       总数
     * @param mCurrentStepNum 当前数
     */
    public void setCurrentStep(int mTotalNum, int mCurrentStepNum) {

        if (mCurrentStepNum > mTotalNum) {
            mCurrentStepNum = mTotalNum;
        }
        //上次步数所占圆环比例
        float scalePrevious = (float) Integer.parseInt(mStepNum) / mTotalNum;
        float previousAngle = scalePrevious * mEndAngle;

        // 所走步数所占总数的百分比
        float scale = (float) mCurrentStepNum / mTotalNum;
        float currentAngle = scale * mEndAngle;

        mStepNum = String.valueOf(mCurrentStepNum);
        setTextSize(mCurrentStepNum);

        //执行动画
        startAnimation(previousAngle,currentAngle,animationLength);

    }

属性动画

private void startAnimation(float start, float current, int length) {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(start,current);
        valueAnimator.setDuration(length);
        valueAnimator.setTarget(mCurrentAngle);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentAngle = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.start();
    }

下面附上完整代码,望各位大佬指正

//圆弧自定义view

public class ArcView extends View {

    /**
     * 圆弧的宽度
     *
     * @param context
     */
    private float mBorderWidth = dipToPx(10);

    /**
     * 中心数字的大小
     *
     * @param context
     */
    private float mTextSize = dipToPx(22);

    /**
     * 背景圆的起始角度
     *
     * @param context
     */
    private float mStartAngle = 90;
    /**
     * 背景圆的结束角度
     *
     * @param context
     */
    private float mEndAngle = 360;

    /**
     * 进度夹角
     *
     * @param context
     */
    private float mCurrentAngle = 0;

    /**
     * 步数
     */
    private String mStepNum = "0";

    /**
     * 动画时长
     */
    private int animationLength = 1000;

    public ArcView(Context context) {
        super(context);
    }

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

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

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


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        /**
         * 中心坐标
         */
        float mCenterX = getWidth() / 2;
        /**
         * 圆弧外轮廓 矩形区域
         */
        RectF mRectF = new RectF(mBorderWidth / 2, mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2, 2 * mCenterX - mBorderWidth / 2);

        //画背景圆弧
        drawArcOne(canvas, mRectF);
        //画进度圆弧
        drawArcTwo(canvas, mRectF);
        //画中间数字
        drawTextNum(canvas, mCenterX);
        //画中间文字
        drawText(canvas, mCenterX);

    }

    /**
     * 绘制文字
     * @param canvas
     * @param mCenterX
     */
    private void drawText(Canvas canvas, float mCenterX) {
        Paint paint = new Paint();
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setColor(getResources().getColor(R.color.black));
        paint.setAntiAlias(true);
        paint.setTextSize(dipToPx(18));
        String s = "步数";
        Rect rect = new Rect();
        paint.getTextBounds(s, 0, 2, rect);
        float biseY = getTextBaseline(paint, getHeight() / 2);

        canvas.drawText(s, mCenterX, biseY + getFontHeight(mTextSize), paint);

    }

    /**
     * 获取当前步数的数字的高度
     *
     * @param fontSize 字体大小
     * @return 字体高度
     */
    public int getFontHeight(float fontSize) {
        Paint paint = new Paint();
        paint.setTextSize(fontSize);
        Rect bounds_Number = new Rect();
        paint.getTextBounds(mStepNum, 0, mStepNum.length(), bounds_Number);
        return bounds_Number.height();
    }

    /**
     * 绘制内层数字
     * @param canvas
     * @param centerX
     */
    private void drawTextNum(Canvas canvas, float centerX) {
        Paint paint = new Paint();
        paint.setTextAlign(Paint.Align.CENTER);
        paint.setAntiAlias(true);
        paint.setTextSize(mTextSize);
        paint.setColor(getResources().getColor(R.color.black));
        //字体类型
        Typeface mTypeFace = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD);
        paint.setTypeface(mTypeFace);
        Rect rect = new Rect();
        paint.getTextBounds(mStepNum, 0, mStepNum.length(), rect);
        //获取文字基线位置
        float biseY = getTextBaseline(paint, getHeight() / 2);
        //为了显示数字之下的文字,在基线坐标基础上减去文字高度的一半
        canvas.drawText(mStepNum, centerX, biseY - rect.height() / 2, paint);
    }

    /**
     * 绘制内层进度圆弧
     * @param canvas
     * @param mRectF
     */
    private void drawArcTwo(Canvas canvas, RectF mRectF) {

        Paint paint = new Paint();
        //画笔颜色
        paint.setColor(getResources().getColor(R.color.blue_light));
        //画笔宽度
        paint.setStrokeWidth(mBorderWidth);
        //画笔抗锯齿
        paint.setAntiAlias(true);
        //Paint.Style.STROKE 仅填充描边
        paint.setStyle(Paint.Style.STROKE);
        //画笔触笔类型 Paint.Cap.ROUND 圆形
        paint.setStrokeCap(Paint.Cap.ROUND);
        //结合处为 Paint.Join.ROUND 圆形
        paint.setStrokeJoin(Paint.Join.ROUND);

        canvas.drawArc(mRectF, mStartAngle, mCurrentAngle, false, paint);
    }


    /**
     * 绘制最外层圆弧
     * @param canvas
     * @param mRectF
     */
    private void drawArcOne(Canvas canvas, RectF mRectF) {

        Paint paint = new Paint();
        //画笔颜色
        paint.setColor(Color.parseColor("#e0f9ff"));
        //画笔结合处类型 圆形
        paint.setStrokeJoin(Paint.Join.ROUND);
        //护臂落笔出类型  圆形
        paint.setStrokeCap(Paint.Cap.ROUND);
        //画笔类型 设置为 仅填充边框
        paint.setStyle(Paint.Style.STROKE);
        //设置抗锯齿
        paint.setAntiAlias(true);
        //设置边框宽度
        paint.setStrokeWidth(mBorderWidth);

        /**
         * mRectF 圆形外矩形
         * mStartAngle 起始角度
         * mEndAngle   结束角度
         */
        canvas.drawArc(mRectF, mStartAngle, mEndAngle, false, paint);

    }


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

    /**
     * dip 转换成px
     *
     * @param dip
     * @return
     */
    private int dipToPx(float dip) {
        float density = getContext().getResources().getDisplayMetrics().density;
        return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1));
    }


    /**
     * 计算绘制文本的基线
     *
     * @param paint      绘制文字的画笔
     * @param halfHeight 高度的一半
     */
    private float getTextBaseline(Paint paint, float halfHeight) {
        Paint.FontMetrics fontMetrics = paint.getFontMetrics();
        float dy = (fontMetrics.bottom - fontMetrics.top) / 2 - fontMetrics.bottom;
        return halfHeight + dy;
    }


    /**
     * 对外的方法
     * @param mTotalNum       总数
     * @param mCurrentStepNum 当前数
     */
    public void setCurrentStep(int mTotalNum, int mCurrentStepNum) {

        if (mCurrentStepNum > mTotalNum) {
            mCurrentStepNum = mTotalNum;
        }
        //上次步数所占圆环比例
        float scalePrevious = (float) Integer.parseInt(mStepNum) / mTotalNum;
        float previousAngle = scalePrevious * mEndAngle;

        // 所走步数所占总数的百分比
        float scale = (float) mCurrentStepNum / mTotalNum;
        float currentAngle = scale * mEndAngle;

        mStepNum = String.valueOf(mCurrentStepNum);
        setTextSize(mCurrentStepNum);

        //执行动画
        startAnimation(previousAngle,currentAngle,animationLength);

    }

    private void startAnimation(float start, float current, int length) {
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(start,current);
        valueAnimator.setDuration(length);
        valueAnimator.setTarget(mCurrentAngle);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentAngle = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.start();
    }

    /**
     * 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置
     *
     * @param num
     */
    public void setTextSize(int num) {
        String s = String.valueOf(num);
        int length = s.length();
        if (length <= 4) {
            mTextSize = dipToPx(30);
        } else if (length > 4 && length <= 6) {
            mTextSize = dipToPx(25);
        } else if (length > 6 && length <= 8) {
            mTextSize = dipToPx(24);
        } else if (length > 8) {
            mTextSize = dipToPx(22);
        }
    }

}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值