Android仪表盘效果

效果如图,带动画

实现步骤,只需四步:

1.在res/values文件夹下新建attrs.xml文件,自定义属性如下:

<?xml version="1.0" encoding="utf-8"?>

    <attr name="color_dial_lower" format="color"/>

    <attr name="color_dial_middle" format="color"/>

    <attr name="color_dial_high" format="color"/>

    <attr name="text_size_dial" format="dimension"/>

    <attr name="stroke_width_dial" format="dimension"/>

    <attr name="radius_circle_dial" format="dimension"/>

    <attr name="text_title_dial" format="string"/>

    <attr name="text_title_size" format="dimension"/>

    <attr name="text_title_color" format="color"/>

    <attr name="text_size_value" format="dimension"/>

    <attr name="animator_play_time" format="integer"/>

    <declare-styleable name="ClockView">

        <attr name="color_dial_lower"/>

        <attr name="color_dial_middle"/>

        <attr name="color_dial_high"/>

        <attr name="text_size_dial"/>

        <attr name="stroke_width_dial"/>

        <attr name="radius_circle_dial"/>

        <attr name="text_title_dial"/>

        <attr name="text_title_size"/>

        <attr name="text_title_color"/>

        <attr name="text_size_value"/>

        <attr name="animator_play_time"/>

</declare-styleable>

</resources>

2.新建自定义控件ClockView.java:

public class ClockViewextends View {

private static final int DEFAULT_COLOR_LOWER = Color.parseColor("#ffffff");//下游颜色

    private static final int DEFAULT_COLOR_MIDDLE = Color.parseColor("#ffffff");//中间颜色

    private static final int DEFAULT_COLOR_HIGH = Color.parseColor("#ffffff");//高的颜色

    private static final int DEAFAULT_COLOR_TITLE = Color.parseColor("#ffffff");//标题颜色

    private static final int DEFAULT_TEXT_SIZE_DIAL =8;//仪表盘字体大小

    private static final int DEFAULT_STROKE_WIDTH =3;//仪表盘线的宽度

    private static final int DEFAULT_RADIUS_DIAL =128;//转盘半径

    private static final int DEAFAULT_TITLE_SIZE =10;//标题大小

    private static final int DEFAULT_VALUE_SIZE =14;//下面百分比字体 值的大小

    private static final int DEFAULT_ANIM_PLAY_TIME =2000;//动画时间

    private int colorDialLower;//转盘下游颜色

    private int colorDialMiddle;//转盘中游颜色

    private int colorDialHigh;//转盘上游颜色

    private int textSizeDial;//转盘文字大小

    private int strokeWidthDial;//转盘中风宽度

    private StringtitleDial;//转盘标题

    private int titleDialSize;//转盘标题大小

    private int titleDialColor;//转盘标题颜色

    private int valueTextSize;//值的大小

    private int animPlayTime;//动画时间

    private int radiusDial;//转盘半径

    private int mRealRadius;//实际半径

    private float currentValue;//当前值

    private PaintarcPaint;//弧的画笔

    private RectFmRect;//矩形

    private PaintpointerPaint;//指针

    private Paint.FontMetricsfontMetrics;//字体度量

    private PainttitlePaint;//标题画笔

    private PathpointerPath;//指示器路径

    private float mDegreesRotate ; //每一份的角度

    public ClockView(Context context) {

this(context, null);

    }

public ClockView(Context context, @Nullable AttributeSet attrs) {

this(context, attrs, 0);

    }

public ClockView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {

super(context, attrs, defStyleAttr);

        initAttrs(context, attrs);

        initPaint();

    }

private void initAttrs(Context context, AttributeSet attrs){

//获得样式属性

        TypedArray attributes = context.obtainStyledAttributes(attrs,R.styleable.ClockView);

        colorDialLower = attributes.getColor(R.styleable.ClockView_color_dial_lower, DEFAULT_COLOR_LOWER);//转盘下游颜色

        colorDialMiddle = attributes.getColor(R.styleable.ClockView_color_dial_middle, DEFAULT_COLOR_MIDDLE);//转盘中游颜色

        colorDialHigh = attributes.getColor(R.styleable.ClockView_color_dial_high, DEFAULT_COLOR_HIGH);//转盘上游颜色

        textSizeDial = (int) attributes.getDimension(R.styleable.ClockView_text_size_dial, sp2px(DEFAULT_TEXT_SIZE_DIAL));//文字大小

        strokeWidthDial = (int) attributes.getDimension(R.styleable.ClockView_stroke_width_dial, dp2px(DEFAULT_STROKE_WIDTH));//线条宽度

        radiusDial = (int) attributes.getDimension(R.styleable.ClockView_radius_circle_dial, dp2px(DEFAULT_RADIUS_DIAL));//转盘半径周期

        titleDial = attributes.getString(R.styleable.ClockView_text_title_dial);//转盘标题

        titleDialSize = (int) attributes.getDimension(R.styleable.ClockView_text_title_size, dp2px(DEAFAULT_TITLE_SIZE));//转盘标题大小

        titleDialColor = attributes.getColor(R.styleable.ClockView_text_title_color, DEAFAULT_COLOR_TITLE);//转盘标题颜色

        valueTextSize = (int) attributes.getDimension(R.styleable.ClockView_text_size_value, dp2px(DEFAULT_VALUE_SIZE));//转盘值

        animPlayTime = attributes.getInt(R.styleable.ClockView_animator_play_time, DEFAULT_ANIM_PLAY_TIME);//动画时间

    }

private void initPaint(){

//圆弧画笔

        arcPaint =new Paint();

        arcPaint.setAntiAlias(true);//抗锯齿

        arcPaint.setStyle(Paint.Style.STROKE);//风格

        arcPaint.setStrokeWidth(strokeWidthDial);//转盘中风宽度

//指针画笔

        pointerPaint =new Paint();

        pointerPaint.setAntiAlias(true);//抗锯齿

        pointerPaint.setTextSize(textSizeDial);//文字大小

        pointerPaint.setTextAlign(Paint.Align.CENTER);//排成一行 居中

        fontMetrics =pointerPaint.getFontMetrics();//获得字体度量

//标题画笔

        titlePaint =new Paint();

        titlePaint.setAntiAlias(true);//抗锯齿

        titlePaint.setTextAlign(Paint.Align.CENTER);//排成一行 居中

        titlePaint.setFakeBoldText(true);//设置黑体

//指针条

        pointerPath =new Path();

    }

@Override

    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int widthMode = MeasureSpec.getMode(widthMeasureSpec);//获得测量宽的模式

        int widthSize = MeasureSpec.getSize(widthMeasureSpec);//获得测量宽的大小

        int heightMode = MeasureSpec.getMode(heightMeasureSpec);//获得测量高的模式

        int heightSize = MeasureSpec.getSize(heightMeasureSpec);//获得测量高的大小

        int mWidth, mHeight;

        if (widthMode == MeasureSpec.EXACTLY){//精确的

            mWidth = widthSize;

        }else {

mWidth = getPaddingLeft() +radiusDial *2 + getPaddingRight();

            if (widthMode == MeasureSpec.AT_MOST){//大概

                mWidth = Math.min(mWidth, widthSize);

            }

}

if (heightMode == MeasureSpec.EXACTLY){//精确的

            mHeight = heightSize;

        }else {

mHeight = getPaddingTop() +radiusDial *2 + getPaddingBottom();

            if (heightMode == MeasureSpec.AT_MOST){//大概

                mHeight = Math.min(mHeight, heightSize);

            }

}

//设置测量的大小

        setMeasuredDimension(mWidth, mHeight);

        radiusDial = Math.min((getMeasuredWidth() - getPaddingLeft() - getPaddingRight()),

                (getMeasuredHeight() - getPaddingTop() - getPaddingBottom())) /2;

        mRealRadius =radiusDial -strokeWidthDial /2;//真实的半径

        mRect =new RectF(-mRealRadius, -mRealRadius, mRealRadius, mRealRadius);//矩形 左上右下

    }

@Override

    protected void onDraw(Canvas canvas) {

super.onDraw(canvas);

        drawArc(canvas);//画弧

        drawPointerLine(canvas);//画指针线

        drawTitleDial(canvas);//画标题

        drawPointer(canvas);//画指针

    }

//画弧

    private void drawArc(Canvas canvas){

//画布转换

        canvas.translate(getPaddingLeft() +radiusDial, getPaddingTop() +radiusDial);

        arcPaint.setColor(colorDialLower);//转盘下游颜色

        canvas.drawArc(mRect, 135, 54, false, arcPaint);

        arcPaint.setColor(colorDialMiddle);//转盘中游颜色

        canvas.drawArc(mRect, 189, 162, false, arcPaint);

        arcPaint.setColor(colorDialHigh);//转盘高游颜色

        canvas.drawArc(mRect, 351, 54, false, arcPaint);

    }

//画指针线 默认100 份

    private void drawPointerLine(Canvas canvas){

float degreesRotate =2.7f ; //每一份的角度

        mDegreesRotate = degreesRotate;

        //画布旋转

        canvas.rotate(135);

        for (int i=0; i<101; i++){//一共需要绘制101个表针

            if (i <=20){

pointerPaint.setColor(colorDialLower);

            }else if (i<=80){

pointerPaint.setColor(colorDialMiddle);

            }else {

pointerPaint.setColor(colorDialHigh);

            }

if (i %10 ==0){//长表针

                pointerPaint.setStrokeWidth(2);

                canvas.drawLine(radiusDial, 0, radiusDial -strokeWidthDial - dp2px(15), 0, pointerPaint);

                drawPointerText(canvas, i, degreesRotate);

            }else {//短表针

                pointerPaint.setStrokeWidth(1);

                canvas.drawLine(radiusDial, 0, radiusDial -strokeWidthDial - dp2px(5), 0, pointerPaint);

            }

canvas.rotate(degreesRotate);

        }

}

//画指针线 260 份

    private void drawPointerLine260(Canvas canvas){

float degreesRotate =1.04f ; //每一份的角度

        mDegreesRotate = degreesRotate;

        //画布旋转

        canvas.rotate(135);

        for (int i=0; i<261; i++){//一共需要绘制261个表针

            if (i <=20){

pointerPaint.setColor(colorDialLower);

            }else if (i<=80){

pointerPaint.setColor(colorDialMiddle);

            }else {

pointerPaint.setColor(colorDialHigh);

            }

if (i %20 ==0){//长表针

//                pointerPaint.setStrokeWidth(6);

                pointerPaint.setStrokeWidth(2);

                canvas.drawLine(radiusDial, 0, radiusDial -strokeWidthDial - dp2px(15), 0, pointerPaint);

                drawPointerText(canvas, i, degreesRotate);

            }else if (i %2 ==0){//短表针

//                pointerPaint.setStrokeWidth(3);

                pointerPaint.setStrokeWidth(1);

                canvas.drawLine(radiusDial, 0, radiusDial -strokeWidthDial - dp2px(5), 0, pointerPaint);

            }

canvas.rotate(degreesRotate);

        }

}

//画指针文字

    private void drawPointerText(Canvas canvas, int i, float degreesRotate){

canvas.save();

        int currentCenterX = (int) (radiusDial -strokeWidthDial - dp2px(21) -pointerPaint.measureText(String.valueOf(i)) /2);

        canvas.translate(currentCenterX, 0);

        canvas.rotate(360 -135 - degreesRotate * i);        //坐标系总旋转角度为360度

        int textBaseLine = (int) (0 + (fontMetrics.bottom -fontMetrics.top) /2 -fontMetrics.bottom);

        canvas.drawText(String.valueOf(i), 0, textBaseLine, pointerPaint);

        canvas.restore();

    }

//画标题的值

    private void drawTitleDial(Canvas canvas){

titlePaint.setColor(titleDialColor);

        titlePaint.setTextSize(titleDialSize);

        canvas.rotate( -47.7f);      //恢复坐标系为起始中心位置

        canvas.drawText(titleDial, 0, -radiusDial /3, titlePaint);

        if (currentValue <=20){

titlePaint.setColor(colorDialLower);

        }else if (currentValue <=80){

titlePaint.setColor(colorDialMiddle);

        }else {

titlePaint.setColor(colorDialHigh);

        }

titlePaint.setTextSize(valueTextSize);

        canvas.drawText(currentValue +"%", 0, radiusDial *2/3, titlePaint);

    }

//画旋转的指针

    private void drawPointer(Canvas canvas){

int currentDegree = (int) (currentValue *mDegreesRotate +135);

        canvas.rotate(currentDegree);

        pointerPath.moveTo(radiusDial -strokeWidthDial - dp2px(12), 0);

        pointerPath.lineTo(0, -dp2px(5));

        pointerPath.lineTo(-12, 0);

        pointerPath.lineTo(0, dp2px(5));

        pointerPath.close();

        canvas.drawPath(pointerPath,titlePaint);

    }

//设置完成程度

    public void setCompleteDegree(float degree){

ValueAnimator animator = ValueAnimator.ofFloat(0, degree);

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

            public void onAnimationUpdate(ValueAnimator animation) {

currentValue = (float)(Math.round((float) animation.getAnimatedValue() *100)) /100;

                invalidate();

            }

});

        animator.setInterpolator(new AccelerateDecelerateInterpolator());

        animator.setDuration(animPlayTime);

        animator.start();

    }

protected int dp2px(int dpVal) {

return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics());

    }

protected int sp2px(int spVal) {

return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics());

    }

public void setNum(float degree){

currentValue = degree;

        invalidate();

    }

//动态设置指针最终位置,附带动画效果

    public void setNumAnimator(float degree){

ValueAnimator animator = ValueAnimator.ofFloat(currentValue, degree);

        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {

@Override

            public void onAnimationUpdate(ValueAnimator animation) {

currentValue = (float)(Math.round((float) animation.getAnimatedValue() *100)) /100;

                invalidate();

            }

});

        animator.setInterpolator(new AccelerateDecelerateInterpolator());

        animator.setDuration(1000);

        animator.start();

    }

}

3.xml里面使用控件:

<LinearLayout

    android:layout_width="match_parent"

    android:layout_height="match_parent"

    android:gravity="center_vertical"

    android:paddingLeft="10dp"

    android:paddingRight="10dp"

    android:orientation="horizontal">

        android:id="@+id/clock_view"

        android:layout_width="0dp"

        android:layout_weight="1"

        android:layout_marginRight="20dp"

        android:layout_height="wrap_content"

        app:text_title_dial="完成率"

        />

        android:id="@+id/clock_view2"

        android:layout_width="0dp"

        android:layout_marginLeft="20dp"

        android:layout_weight="1"

        android:layout_height="wrap_content"

        app:text_title_dial="完成率"/>

</LinearLayout>

4.代码动态设置值:

左图:

clockView.setCompleteDegree(32.25f); //设置指针最终位置,附带动画效果

右图:

String aaa ="50.12";

float num1 = Float.valueOf(aaa);

clockView2.setNumAnimator(num1);  //设置指针最终位置,附带动画效果

到这里就开发完成了,可以看看效果。

如果所需刻度总数不是100,代码里面有个刻度总数260的方法,可以参照修改。

参考:

https://www.cnblogs.com/changyiqiang/p/10874639.html

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。