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

自定义ProgressBar-油表,汽车速度表控件

程序员文章站 2022-06-21 19:49:21
一、写在前面的话今天做了个控件,觉得还挺漂亮,就拿出来给大家分享一下,主要为了给大家介绍一下ValueAnimator这个动画,确实也没啥好说的,网上一堆教程,还是说说说画这个动画好了。二、需求分析刚拿到需求,我们分析一下。控件有两道圆弧,第一道上面有数据段,第二段是具体数值的指示段。数值变换的时候,需要平滑过渡,不能突兀。三、开始画。1、画圆弧 private void drawArc(Canvas canvas) { mBgPaint.setStrok.....

自定义ProgressBar-油表,汽车速度表控件

一、写在前面的话

今天做了个控件,觉得还挺漂亮,就拿出来给大家分享一下,主要为了给大家介绍一下ValueAnimator这个动画,确实也没啥好说的,网上一堆教程,还是说说说画这个动画好了。

二、需求分析

刚拿到需求,我们分析一下。控件有两道圆弧,第一道上面有数据段,第二段是具体数值的指示段。

数值变换的时候,需要平滑过渡,不能突兀。

三、开始画。

1、画圆弧

  private void drawArc(Canvas canvas) {
        mBgPaint.setStrokeWidth(mProgressSize);
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150, 240, false, mBgPaint);
        //画推荐度数
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + lowProgress * 240 / 100, (highProgress - lowProgress) * 240 / 100, false, mProgressPaint);
        //画推荐度数的值
        int r = mWidth / 2 - mProgressSize;
        int x = getArcX(150 + lowProgress * 240 / 100, r);
        int y = getArcY(150 + lowProgress * 240 / 100, r);
        int x2 = getArcX(150 + highProgress * 240 / 100, r);
        int y2 = getArcY(150 + highProgress * 240 / 100, r);
        if (x < mWidth / 2) {
            x = x - 13;
        } else {
            y = y + 7;
            x = x - 3;

        }
        if (x2 < mWidth / 2) {
            x2 = x2 - 13;
        } else {
            x2 = x2 - 3;

            y2 = y2 + 7;

        }
        canvas.drawText(stringLowProgress, x, y, mTextPaint);
        canvas.drawText(stringHighProgress, x2, y2, mTextPaint);

        mBgPaint.setStrokeWidth(mProgressSize / 3);
        canvas.drawArc(mProgressSize * offset, mProgressSize * offset, mWidth - mProgressSize * offset, mHeight - mProgressSize * offset, 150, 240, false, mBgPaint);
    }

为什么会/100呢,因为这里我把进度放大了一百倍,为了值的变化更流畅。

2、画刻度

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void drawLine(Canvas canvas) {
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 6 - 1, 2, false, mWhitePaint);
        for (int i = 1; i < 10; i++) {
            canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 24 * i - 1, 2, false, mWhitePaint);
        }
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 30 - 6 - 1, 2, false, mWhitePaint);
    }

这里的刻度我们画一小段白色的圆弧,大家可以根据自己的要求改一下。

3、画指针


    private void drawProgress(Canvas canvas) {
        //计算当前的角度
        int angle = 150 + mProgress * 240 / mMaxProgress;
        int r = mWidth / 2 - mProgressSize * offset;
        int cx = getArcX(angle, r);
        int cy = getArcY(angle, r);
        canvas.drawCircle(cx, cy, 4, mProgressPaint);

        //圆外一点的坐标
        int x = getArcX(angle, r + 35);
        int y = getArcY(angle, r + 35);

        //控制点1
        int co1x = getArcX(angle - 4, r + 5);
        int co1y = getArcY(angle - 4, r + 5);
        //控制点2
        int co2x = getArcX(angle + 4, r + 5);
        int co2y = getArcY(angle + 4, r + 5);
        path.reset();
        path.moveTo(co1x, co1y);
        path.lineTo(co2x, co2y);
        path.lineTo(x, y);
        path.lineTo(co1x, co1y);
        canvas.drawPath(path, mSmallWhitePaint);
        canvas.drawCircle(cx, cy, 1, mWhitePaint);


    }

4、指针动画

    //    /**
//     * 进度显示动画效果初始化
//     */
    private void initValueAnimator() {
        valueAnimator = new ValueAnimator();
        valueAnimator.setInterpolator(interpolator);
        valueAnimator.addUpdateListener(new ValueAnimatorListenerImp());
    }

    /**
     * 动画开始 更新进度显示
     */
    private class ValueAnimatorListenerImp implements ValueAnimator.AnimatorUpdateListener {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            int value = Math.round((Float) valueAnimator.getAnimatedValue());
            mProgress = value;
//            if (valueChangeListener != null) {
//                valueChangeListener.onValueChange(value);
//            }
            postInvalidate();
        }

    }
    //进度条的值会放大一百倍,为了使改变动画的帧率更高
    public void setProgress(int progress) {
        if (System.currentTimeMillis() - lastTime < duration) {
            return;
        }
        lastTime = System.currentTimeMillis();
//        this.mProgress = progress * 100;
        if (valueAnimator != null) {
            valueAnimator.setFloatValues(mProgress, progress * 100);
            valueAnimator.setDuration(duration);
            valueAnimator.start();
        }
    }

5、布局文件

   <androidx.cardview.widget.CardView
                android:layout_width="@dimen/dp_180"
                android:layout_height="@dimen/dp_180"
                android:layout_margin="@dimen/text_margin"
                android:background="@color/white"
                android:elevation="@dimen/dp_5"
                app:cardCornerRadius="@dimen/dp_90"
                tools:ignore="UnusedAttribute">

                <RelativeLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent">

                    <com.jack.heart.test.widget.SmartCircleProgress
                        android:id="@+id/scp_o2"
                        android:layout_width="match_parent"
                        android:layout_height="match_parent"
                        app:color="@color/color_a"
                        app:highProgress="100"
                        app:lowProgress="80"
                        app:progressColor="@color/color_a_progress"
                        app:stringHighProgress="99"
                        app:stringLowProgress="80" />

                    <TextView
                        android:id="@+id/tv_o2_values"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_centerInParent="true"
                        android:text="--"
                        android:textColor="@color/color_a_progress"
                        android:textSize="@dimen/sp_30"
                        android:textStyle="bold" />

                    <TextView
                        android:id="@+id/tv_o2_pce"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_below="@+id/tv_o2_values"
                        android:layout_centerHorizontal="true"
                        android:text="L/100km"
                        android:textColor="@color/color_text_hint"
                        android:textSize="@dimen/text_3" />

                    <TextView
                        android:id="@+id/tv_o2_name"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:layout_below="@+id/tv_o2_pce"
                        android:layout_centerHorizontal="true"
                        android:text="百公里耗油量"
                        android:textColor="@color/color_text_black1"
                        android:textSize="@dimen/text_3" />

                    <ImageView
                        android:layout_width="@dimen/dp_20"
                        android:layout_height="@dimen/dp_20"
                        android:visibility="gone"
                        android:layout_below="@+id/tv_o2_name"
                        android:layout_centerHorizontal="true"
                        android:background="@mipmap/measure_icon_trend_spo2" />

                </RelativeLayout>
            </androidx.cardview.widget.CardView>

 用CardView做外面的阴影效果,因为很简单,就没有封装了。

四、结束

说完了,因为内容比较简单,有点词穷了,所以基本都是贴代码了。最后会附上源码的,直接copy就可以用了,大家如果有啥想开发的控件都可以私信我,过年回家敲着玩。


import android.animation.ValueAnimator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Build;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Interpolator;

import androidx.annotation.Nullable;
import androidx.annotation.RequiresApi;




public class SmartCircleProgress extends View {
    private int mWidth = 0;
    private int mHeight = 0;
    private Paint mBgPaint;
    private Paint mProgressPaint;
    private Paint mWhitePaint;
    private Paint mSmallWhitePaint;
    private Paint mTextPaint;


    private int mProgressSize = 25;

    private int mProgress = 0;
    private int begin = 0;
    private int mMaxProgress = 100;
    //最外层圆弧相对边距的距离的比
    private int lineSize = 2;
    //最内层圆弧相对边距的距离的比
    private int offset = 5;

    private Path path;

    private int duration = 400;

    private int type = 0;
    private Interpolator interpolator = new DecelerateInterpolator();
    private ValueAnimator valueAnimator;

    private long lastTime = System.currentTimeMillis();

    private int highProgress = 0;
    private int lowProgress = 0;

    private String stringHighProgress = "";
    private String stringLowProgress = "";

    public SmartCircleProgress(Context context) {

        super(context);
        init(context, null);
    }

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

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

    @SuppressLint("Recycle")
    private void init(Context context, AttributeSet attrs) {
        TypedArray s = null;
        if (attrs != null) {
            s = context.obtainStyledAttributes(attrs, R.styleable.SmartCircleProgress);//从xml那传来的一组值
        }

        mBgPaint = new Paint();
        mBgPaint.setColor(getResources().getColor(R.color.color_a));
        mBgPaint.setDither(true);
        mBgPaint.setStyle(Paint.Style.STROKE);
        mBgPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mBgPaint.setAntiAlias(true);
        mBgPaint.setStrokeCap(Paint.Cap.ROUND);
        mBgPaint.setStrokeWidth(mProgressSize);

        mTextPaint = new Paint();
        mTextPaint.setColor(getResources().getColor(R.color.color_a));
        mTextPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mTextPaint.setAntiAlias(true);
        mTextPaint.setTextSize(20);

        mWhitePaint = new Paint();
        mWhitePaint.setColor(Color.WHITE);
        mWhitePaint.setDither(true);
        mWhitePaint.setStyle(Paint.Style.STROKE);
        mWhitePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mWhitePaint.setAntiAlias(true);
        mWhitePaint.setStrokeWidth(mProgressSize);

        mSmallWhitePaint = new Paint();
        mSmallWhitePaint.setColor(getResources().getColor(R.color.color_a_progress));
        mSmallWhitePaint.setDither(true);
        mSmallWhitePaint.setStyle(Paint.Style.FILL);
        mSmallWhitePaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mSmallWhitePaint.setAntiAlias(true);
        mSmallWhitePaint.setStrokeWidth(1);

        mProgressPaint = new Paint();
        mProgressPaint.setColor(getResources().getColor(R.color.color_a_progress));
        mProgressPaint.setDither(true);
        mProgressPaint.setStyle(Paint.Style.STROKE);
        mProgressPaint.setFlags(Paint.ANTI_ALIAS_FLAG);
        mProgressPaint.setAntiAlias(true);
        mProgressPaint.setStrokeCap(Paint.Cap.ROUND);
        mProgressPaint.setStrokeWidth(mProgressSize);

        path = new Path();

        if (s != null) {
            //背景画笔
            mBgPaint.setColor(s.getColor(R.styleable.SmartCircleProgress_color, getResources().getColor(R.color.color_a)));

            //小细线画笔(箭头)
            mSmallWhitePaint.setColor(s.getColor(R.styleable.SmartCircleProgress_progressColor, getResources().getColor(R.color.color_a_progress)));
            //进度画笔
            mProgressPaint.setColor(s.getColor(R.styleable.SmartCircleProgress_progressColor, getResources().getColor(R.color.color_a_progress)));
            mTextPaint.setColor(s.getColor(R.styleable.SmartCircleProgress_progressColor, getResources().getColor(R.color.color_a_progress)));


            lowProgress = s.getInteger(R.styleable.SmartCircleProgress_lowProgress, 0);
            highProgress = s.getInteger(R.styleable.SmartCircleProgress_highProgress, 0);

            stringHighProgress = s.getString(R.styleable.SmartCircleProgress_stringHighProgress);
            stringLowProgress = s.getString(R.styleable.SmartCircleProgress_stringLowProgress);

        }
        initValueAnimator();

    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        mWidth = w;
        mHeight = h;
//        mProgressSize = w / 18;
//        mBgPaint.setStrokeWidth(mProgressSize);
//        mProgressPaint.setStrokeWidth(mProgressSize);

    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawArc(canvas);
        drawLine(canvas);

        drawProgress(canvas);
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void drawLine(Canvas canvas) {
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 6 - 1, 2, false, mWhitePaint);
        for (int i = 1; i < 10; i++) {
            canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + 24 * i - 1, 2, false, mWhitePaint);
        }
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 30 - 6 - 1, 2, false, mWhitePaint);
    }


    public int getProgress() {
        return mProgress / 100;
    }

    //进度条的值会放大一百倍,为了使改变动画的帧率更高
    public void setProgress(int progress) {
        if (System.currentTimeMillis() - lastTime < duration) {
            return;
        }
        lastTime = System.currentTimeMillis();
//        this.mProgress = progress * 100;
        if (valueAnimator != null) {
            valueAnimator.setFloatValues(mProgress, progress * 100);
            valueAnimator.setDuration(duration);
            valueAnimator.start();
        }
    }

    public int getMaxProgress() {
        return mMaxProgress / 100;
    }

    public void setMaxProgress(int mMaxProgress) {
        this.mMaxProgress = mMaxProgress * 100;
        postInvalidate();
    }

    @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
    private void drawArc(Canvas canvas) {
        mBgPaint.setStrokeWidth(mProgressSize);
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150, 240, false, mBgPaint);
        //画推荐度数
        canvas.drawArc(mProgressSize * lineSize, mProgressSize * lineSize, mWidth - mProgressSize * lineSize, mHeight - mProgressSize * lineSize, 150 + lowProgress * 240 / 100, (highProgress - lowProgress) * 240 / 100, false, mProgressPaint);
        //画推荐度数的值
        int r = mWidth / 2 - mProgressSize;
        int x = getArcX(150 + lowProgress * 240 / 100, r);
        int y = getArcY(150 + lowProgress * 240 / 100, r);
        int x2 = getArcX(150 + highProgress * 240 / 100, r);
        int y2 = getArcY(150 + highProgress * 240 / 100, r);
        if (x < mWidth / 2) {
            x = x - 13;
        } else {
            y = y + 7;
            x = x - 3;

        }
        if (x2 < mWidth / 2) {
            x2 = x2 - 13;
        } else {
            x2 = x2 - 3;

            y2 = y2 + 7;

        }
        canvas.drawText(stringLowProgress, x, y, mTextPaint);
        canvas.drawText(stringHighProgress, x2, y2, mTextPaint);

        mBgPaint.setStrokeWidth(mProgressSize / 3);
        canvas.drawArc(mProgressSize * offset, mProgressSize * offset, mWidth - mProgressSize * offset, mHeight - mProgressSize * offset, 150, 240, false, mBgPaint);
    }

    private int getArcX(int angle, int r) {
        return (int) (mWidth / 2 + r * Math.cos(angle * Math.PI / 180));
    }

    private int getArcY(int angle, int r) {
        return (int) (mWidth / 2 + r * Math.sin(angle * Math.PI / 180));
    }

    private void drawProgress(Canvas canvas) {
        //计算当前的角度
        int angle = 150 + mProgress * 240 / mMaxProgress;
        int r = mWidth / 2 - mProgressSize * offset;
        int cx = getArcX(angle, r);
        int cy = getArcY(angle, r);
        canvas.drawCircle(cx, cy, 4, mProgressPaint);

        //圆外一点的坐标
        int x = getArcX(angle, r + 35);
        int y = getArcY(angle, r + 35);

        //控制点1
        int co1x = getArcX(angle - 4, r + 5);
        int co1y = getArcY(angle - 4, r + 5);
        //控制点2
        int co2x = getArcX(angle + 4, r + 5);
        int co2y = getArcY(angle + 4, r + 5);
        path.reset();
        path.moveTo(co1x, co1y);
        path.lineTo(co2x, co2y);
        path.lineTo(x, y);
        path.lineTo(co1x, co1y);
        canvas.drawPath(path, mSmallWhitePaint);
        canvas.drawCircle(cx, cy, 1, mWhitePaint);


    }

    //    /**
//     * 进度显示动画效果初始化
//     */
    private void initValueAnimator() {
        valueAnimator = new ValueAnimator();
        valueAnimator.setInterpolator(interpolator);
        valueAnimator.addUpdateListener(new ValueAnimatorListenerImp());
    }

    /**
     * 动画开始 更新进度显示
     */
    private class ValueAnimatorListenerImp implements ValueAnimator.AnimatorUpdateListener {
        @Override
        public void onAnimationUpdate(ValueAnimator valueAnimator) {
            int value = Math.round((Float) valueAnimator.getAnimatedValue());
            mProgress = value;
//            if (valueChangeListener != null) {
//                valueChangeListener.onValueChange(value);
//            }
            postInvalidate();
        }

    }

}
attrs.xml
    <declare-styleable name="SmartCircleProgress">
        <attr name="textColor" format="color"></attr>
        <attr name="color" format="color"></attr>
        <attr name="progressColor" format="color"></attr>
        <attr name="max" format="integer"></attr>
        <attr name="lowProgress" format="integer"></attr>
        <attr name="highProgress" format="integer"></attr>
        <attr name="stringLowProgress" format="string"></attr>
        <attr name="stringHighProgress" format="string"></attr>
    </declare-styleable>

 

本文地址:https://blog.csdn.net/qwe749082787/article/details/112801141