自定义View画仪表盘(可画指针,可在中间写刻度值)

自定义View  画仪表盘,其实很好画,代码里都加了注释了,这里就不多写了,根据实际需要改动就可以,可区分不同刻度区域的,一个是带指针的,一个是不带指针在刻度盘中间写实际数值的,先来张示例图看下:

  

画这个仪表盘的步骤:

1、先获取控件区域的宽高,然后找到圆心点以及圆的半径: 

    int minimumWidth = getSuggestedMinimumWidth();
    int minimumHeight = getSuggestedMinimumHeight();
    mWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
    mHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
    // 获取x/y轴中心点
    mCx = mWidth / 2;
    mCy = mHeight / 2;
    radius = Math.min(mWidth, mHeight) /2;//半径



    private int resolveMeasured(int measureSpec, int desired) {
        int specSize = MeasureSpec.getSize(measureSpec);//获取View的大小
        int result;
        switch (MeasureSpec.getMode(measureSpec)) {
            case MeasureSpec.UNSPECIFIED://不对View大小做限制,如:ListView,ScrollView
                result = desired;
                break;
            case MeasureSpec.AT_MOST://大小不可超过某数值,如:wrap_content
                result = Math.max(specSize, desired);
                break;
            case MeasureSpec.EXACTLY://确切的大小,如:100dp或者march_parent
                result = specSize;
                break;
            default:
                result = specSize;
                break;
        }
        return result;
    }

2、画了两个圆背景,按半径的百分比画的,一个大一个小:

        canvas.save();//用来保存canvas的状态
        bgRoundPaint.setStrokeWidth(2f);//设置描边宽度
        bgRoundPaint.setColor(bgRoundColor);//设置画笔颜色
        bgRoundPaint.setStyle(Paint.Style.FILL_AND_STROKE);// 描边加填充  画的实心圆
        canvas.drawCircle(mCx,mCy,radius,bgRoundPaint);
        bgRoundPaint.setColor(bgRoundColor2);
        canvas.drawCircle(mCx,mCy, (float) (radius*0.9),bgRoundPaint);
        canvas.restore();

3、画扇形刻度点:

    //画扇形刻度点
    private void drawPointerLine(Canvas canvas){
        canvas.save();//保存之前的画布状态
        canvas.translate(mCx, mCy);//把当前画布的原点移到(mCx, mCy),后面的操作都以(mCx, mCy)作为参照点,默认原点为(0,0)
        canvas.rotate(arcStartDegree);
        for (int i=0; i<=valRange; i++){//这里这个valRange是最大刻度减去最小刻度,总共需要画的刻度数,可动态更改
            if (i < lowDialLimit){
                pointerPaint.setColor(colorDialLower);
            }else if (i >= lowDialLimit && i <= middleDialLimit){
                pointerPaint.setColor(colorDialMiddleLower);
            }else if (i > middleDialLimit && i<= highDialLimit){
                pointerPaint.setColor(colorDialMiddle);
            }else {
                pointerPaint.setColor(colorDialHigh);
            }
            if (i % dialStep == 0){
                //长刻度
                pointerPaint.setStrokeWidth(strokeLDial);//设置长刻度线粗度
                canvas.drawLine(radiusDial, 0, radiusDial - lengthLDial, 0, pointerPaint);

                String text = String.format("%.0f", minVal + i);
                drawPointerText(canvas, text, i);
            }else {
                //短刻度
                pointerPaint.setStrokeWidth(strokeSDial);
                int offset = (lengthLDial - lengthSDial) / 2;
                canvas.drawLine(radiusDial - offset, 0, radiusDial - lengthSDial, 0, pointerPaint);
            }
            canvas.rotate(sRoateDegree);//每画一个刻度旋转一次画布
        }
        canvas.restore();
    }

4、画渐变色的扇形进度:

void init(){
    // 长刻度线
    radiusDial = (int) (radius * 0.8);
    mArcRect = new RectF(mCx - radiusDial, mCy - radiusDial, mCx + radiusDial, mCy + radiusDial);
            /*
             * Shader.TileMode.REPEAT  平移复制 横向和纵向的重复着色器的图像。
             * Shader.TileMode.CLAMP:会将边缘的一个像素进行拉伸、扩展
             * Shader.TileMode.MIRROR:镜面翻转  横向和纵向的重复着色器的图像,交替镜像图像是相邻的图像总是接合。这个官方的说明可能不太好理解,说白了,就是图像不停翻转来平铺,直到平铺完毕
             * Android 支持三种颜色渐变, LinearGradient(线性渐变)、 RadialGradient (放射渐变)、 SweepGradient(扫描渐变)。这三种渐变均继承自android.graphics.Shader, Paint 类通过setShader()方法来支持渐变
             */
    arcShader = new RadialGradient(mCx, mCy, (float) (radius*0.8), colors, null, Shader.TileMode.REPEAT);
}   



    canvas.save();
    arcPaint.setShader(arcShader);//Paint 类通过setShader()方法来支持渐变
    arcPaint.setStyle(Paint.Style.FILL);
    canvas.drawArc(mArcRect, arcStartDegree, sweepAngle, true, arcPaint);
    canvas.restore();//用来恢复Canvas旋转、缩放等之后的状态,当和canvas.save( )一起使用时,恢复到canvas.save( )保存时的状态

 5、画指针,先初始化绘制一个指针,然后再根据实际刻度值画指针的具体指向角度:

//初始化绘制的指针
void init(){
    //通过path绘制棱形表盘指针
    bmp = Bitmap.createBitmap(20, (int) (radius/2+60), Bitmap.Config.ARGB_8888);
    Canvas canvas = new Canvas(bmp);
    Paint paint6 = new Paint();
    paint6.setAntiAlias(true);
    paint6.setColor(getResources().getColor(R.color.orange));
    Path path = new Path();
    path.moveTo(10,0);
    path.lineTo(20,50);
    path.lineTo(10, (float) (radius/2+60));
    path.lineTo(0,50);
    path.lineTo(10,0);
    canvas.drawPath(path,paint6);
    canvas.drawBitmap(bmp, 170,10, paint6);
    canvas.save();
    canvas.restore();
}


    //画指针
    private void drawPointer(Canvas canvas){
        canvas.save();
        canvas.rotate(row,mCx, mCy);
        pointerPaint.setColor(Color.GREEN);
        canvas.drawBitmap(bmp,mCx-10,mCy-50,pointerPaint);
        pointerPaint.setColor(Color.BLUE);
        canvas.drawCircle(mCx,mCy,6,pointerPaint);
        canvas.restore();
    }

6、表盘底部标题:

    //标题
    labelPaint = new Paint();
    labelPaint.setAntiAlias(true);
    labelPaint.setTextAlign(Paint.Align.CENTER);
    labelPaint.setFakeBoldText(true);//设置文本仿粗体。注意设置在小字体上效果会非常差。
    labelPaint.setTextSize(labelTextSize);
    labelPaint.setColor(labelTextColor);
    labelFontMetrics = labelPaint.getFontMetrics();


    //画表盘底部的标题
    private void drawLabelText(Canvas canvas) {
        canvas.save();
        canvas.translate(mCx, mCy +  radius*0.66f);
        int textBaseLine = (int) (0 + (labelFontMetrics.bottom - labelFontMetrics.top) / 2 - labelFontMetrics.bottom);
        canvas.drawText(labelText, 0, textBaseLine, labelPaint);
        canvas.restore();
    }

 7、画表盘中间写的当前刻度值:

    // 当前刻度值
    valPaint = new Paint();
    valPaint.setAntiAlias(true);
    valPaint.setTextAlign(Paint.Align.CENTER);
    valPaint.setFakeBoldText(true);//设置文本仿粗体。注意设置在小字体上效果会非常差。
    valPaint.setTextSize(valTextSize);
    valPaint.setColor(valTextColor);
    valFontMetrics = valPaint.getFontMetrics();


    //在仪表盘中间写刻度值(如果写刻度值就不能画指针了)
    private void drawValText(Canvas canvas) {
        if(valText != null) {
            canvas.save();
            canvas.translate(mCx, mCy);
            int textBaseLine = (int) (0 + (valFontMetrics.bottom - valFontMetrics.top) / 2 - valFontMetrics.bottom);
            canvas.drawText(valText, 0, textBaseLine, valPaint);
            canvas.restore();
        }
    }

8、提供方法供外部传参使用:

    /**
     * @param labelText  表盘底部的标题
     * @param formatter  中间值格式
     * @param maxVal   最大值
     * @param minVal   最小值
     * @param dialStep 刻度线 必须能被最大范围整除 (maxVal - minVal) / dialStep = 0
     * @param lowDialLimit  第一个警戒线
     * @param middleDialLimit  第二个警戒线
     * @param highDialLimit 第三个警戒线
     * @param val 当前实际刻度值
     */
    public void init(String labelText,String formatter, float maxVal, float minVal,int dialStep, int lowDialLimit, int middleDialLimit, int highDialLimit,float val){
        this.labelText = labelText;
        this.dialStep = dialStep;//大刻度线间距
        float drawDegree = (360 - (180 - arcStartDegree) * 2);
        this.maxVal = maxVal;
        this.minVal = minVal;
        this.valRange = maxVal - minVal;//一共要画的刻度值
        this.formatter = formatter;
        this.drawDialNum = (int) Math.ceil(valRange / dialStep);//要画的大刻度块数
        this.stepDegree =  drawDegree / drawDialNum;//每一大刻度间的角度
        this.lowDialLimit = (int) (lowDialLimit - minVal);
        this.middleDialLimit = (int) (middleDialLimit - minVal);
        this.highDialLimit = (int) (highDialLimit - minVal);
        this.sRoateDegree = stepDegree/dialStep; // 短刻度旋转角度(大刻度间的小刻度角度)

        //这是实际标示的值
        realVal = Math.max(Math.min(val, maxVal), minVal);
        valText = String.format(formatter, realVal);
        sweepAngle = (realVal -minVal) / valRange * (360 - (180 - arcStartDegree) * 2);

        float v = 100.0f / valRange;
        row = 2.7f*(realVal-minVal)*v+45; //计算出的旋转角度,由于前面绘制指针控件的角度是垂直向下的,表盘的起始角度是135度,所以加45度
        invalidate();
    }

下面是完整源码:

package com.example.test1.customView;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.RadialGradient;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;

import androidx.annotation.Nullable;

import com.example.test1.R;
import com.example.test1.utils.LoggUtils;

/**
 * Created by WJY.
 * Date: 2021-10-14
 * Time: 17:43
 * Description: 自定义仪表盘  测试
 */
public class MyDashboardView extends View {

    protected int bgRoundColor = Color.parseColor("#0AF9F6");//背景圆的颜色
    protected int bgRoundColor2 = Color.parseColor("#0da1a1");//背景圆的颜色
    protected int pointColor = Color.parseColor("#00faff"); // 表盘刻度颜色
    protected Paint bgRoundPaint;//背景圆画笔
    protected int mWidth;//宽
    protected int mHeight;//高
    protected int mCx;//圆心X坐标
    protected int mCy;//圆心Y坐标
    protected int radius;//圆的半径

    protected int arcPaintColor = Color.parseColor("#FFB6C1");
    protected Paint arcPaint;
    protected RectF mArcRect;
    protected Shader arcShader;
//    protected int colors[] = new int[]{Color.YELLOW,Color.TRANSPARENT, Color.GREEN, arcPaintColor };//设置渐变色
    protected int colors[] = new int[]{Color.TRANSPARENT,Color.TRANSPARENT, Color.TRANSPARENT, arcPaintColor };//设置渐变色

    protected int radiusDial;// 长刻度线半径
    protected float maxVal = 100f;//最大刻度值
    protected float minVal = 0f;//最小刻度值
    protected float valRange;//一共要画的刻度值(maxVal-minVal)

    private float row;//当前指针

    //下面这三个可设置不同刻度区间显示不同颜色来区分,可以再增加或者减少区间的数量
    protected int lowDialLimit = 20; // 低段温度警戒线
    protected int middleDialLimit = 40; // 中低段温度警戒线
    protected int highDialLimit = 80; // 中高段警戒线
    protected int colorDialLower = Color.parseColor("#00faff"); // 上面三个值分为4个区域,四个色段表盘刻度的颜色
    protected int colorDialMiddleLower = Color.YELLOW;
    protected int colorDialMiddle = Color.GREEN;
    protected int colorDialHigh = Color.RED;

    protected String formatter = "%.0f";//圆盘显示数值的格式
    protected int drawDialNum = 100;//要画的大刻度块数
    protected float stepDegree;//每一大刻度间的角度
    protected float sRoateDegree = 2.7f;// 短刻度旋转角度(大刻度间的小刻度角度)

    protected float arcStartDegree = 135;//起始角度
    protected float sweepAngle = 270;//一共要画的角度数

    // 刻度画笔
    protected Paint pointerPaint;
    protected float textSizeDial = 28; // 刻度字体大小
    protected Paint.FontMetrics pointerFontMetrics;//FontMetrics 字体度量,该类是Paint的内部类,通过getFontMetrics()方法可获取字体相关属性。
    protected int dialStep = 10; // 分度
    protected float strokeLDial = 3;//长刻度线粗度
    protected float strokeSDial = 1;//短刻度线粗度
    protected int lengthLDial = 16;// 长刻度线的长度
    protected int lengthSDial = 8;// 短刻度线的长度

    //画指针用的
    private Bitmap bmp;
    //当前刻度值
    protected Paint valPaint;
    protected float valTextSize = 40f;// 值字体大小
    protected int valTextColor = Color.parseColor("#0AF9F6");// 值字体颜色
    protected float realVal; // 真实的值
    protected String valText="";//显示值
    protected Paint.FontMetrics valFontMetrics;

    //表盘底部的标题
    // 标题画笔
    protected Paint labelPaint;
    protected Paint.FontMetrics labelFontMetrics;
    protected String labelText ="";
    protected float labelTextSize = 30f;// 标签字体
    protected int labelTextColor = Color.WHITE;// 标签字体颜色

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

    public MyDashboardView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,-1);
    }

    public MyDashboardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        this(context, attrs, defStyleAttr,-1);
    }

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

    private void init(){
        /*
        Paint.ANTI_ALIAS_FLAG :抗锯齿标志
        Paint.FILTER_BITMAP_FLAG : 使位图过滤的位掩码标志
        Paint.DITHER_FLAG : 使位图进行有利的抖动的位掩码标志
        Paint.UNDERLINE_TEXT_FLAG : 下划线
        Paint.STRIKE_THRU_TEXT_FLAG : 中划线
        Paint.FAKE_BOLD_TEXT_FLAG : 加粗
        Paint.LINEAR_TEXT_FLAG : 使文本平滑线性扩展的油漆标志
        Paint.SUBPIXEL_TEXT_FLAG : 使文本的亚像素定位的绘图标志
        Paint.EMBEDDED_BITMAP_TEXT_FLAG : 绘制文本时允许使用位图字体的绘图标志
        */
        bgRoundPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

        // 扫描扇
        arcPaint = new Paint();
        arcPaint.setAntiAlias(true);//获取与设置是否使用抗锯齿功能,会消耗较大资源,绘制图形速度会变慢,一般会开启。设置后会平滑一些;

        // 刻度
        pointerPaint = new Paint();
        pointerPaint.setAntiAlias(true);
        pointerPaint.setTextSize(textSizeDial);
        pointerPaint.setTextAlign(Paint.Align.CENTER);
        pointerFontMetrics = pointerPaint.getFontMetrics();

        //标题
        labelPaint = new Paint();
        labelPaint.setAntiAlias(true);
        labelPaint.setTextAlign(Paint.Align.CENTER);
        labelPaint.setFakeBoldText(true);//设置文本仿粗体。注意设置在小字体上效果会非常差。
        labelPaint.setTextSize(labelTextSize);
        labelPaint.setColor(labelTextColor);
        labelFontMetrics = labelPaint.getFontMetrics();

        // 当前刻度值
        valPaint = new Paint();
        valPaint.setAntiAlias(true);
        valPaint.setTextAlign(Paint.Align.CENTER);
        valPaint.setFakeBoldText(true);//设置文本仿粗体。注意设置在小字体上效果会非常差。
        valPaint.setTextSize(valTextSize);
        valPaint.setColor(valTextColor);
        valFontMetrics = valPaint.getFontMetrics();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//        getWidth();
//        getMeasuredWidth();
//        getMinimumWidth();
        //可以看到mBackground  == null 为没有设置背景,那么返回mMinWidth ,也就是android:minWidth 这个属性所指定的值,这个值可以是0 ;如果View 设置了背景,则返回mMinWidth 与背景的最小宽度这两者的最大值。
        //getSuggestedMinimumWidth()的返回值就是View 在UNSPECIFIED 情况下的测量宽。
//        getSuggestedMinimumWidth();

        LoggUtils.e("onMeasure","widthMeasureSpec="+widthMeasureSpec);
        LoggUtils.e("onMeasure","getWidth()="+getWidth());
        LoggUtils.e("onMeasure","getMeasuredWidth()="+getMeasuredWidth());
        LoggUtils.e("onMeasure","getMinimumWidth()="+getMinimumWidth());
        LoggUtils.e("onMeasure","getSuggestedMinimumWidth()="+getSuggestedMinimumWidth());
        // 获取控件区域宽高
        if (mWidth == 0 || mHeight == 0){
            int minimumWidth = getSuggestedMinimumWidth();
            int minimumHeight = getSuggestedMinimumHeight();
            mWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
            mHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
            // 获取x/y轴中心点
            mCx = mWidth / 2;
            mCy = mHeight / 2;
            radius = Math.min(mWidth, mHeight) /2;//半径

            // 长刻度线
            radiusDial = (int) (radius * 0.8);
            mArcRect = new RectF(mCx - radiusDial, mCy - radiusDial, mCx + radiusDial, mCy + radiusDial);
            /*
             * Shader.TileMode.REPEAT  平移复制 横向和纵向的重复着色器的图像。
             * Shader.TileMode.CLAMP:会将边缘的一个像素进行拉伸、扩展
             * Shader.TileMode.MIRROR:镜面翻转  横向和纵向的重复着色器的图像,交替镜像图像是相邻的图像总是接合。这个官方的说明可能不太好理解,说白了,就是图像不停翻转来平铺,直到平铺完毕
             * Android 支持三种颜色渐变, LinearGradient(线性渐变)、 RadialGradient (放射渐变)、 SweepGradient(扫描渐变)。这三种渐变均继承自android.graphics.Shader, Paint 类通过setShader()方法来支持渐变
             */
            arcShader = new RadialGradient(mCx, mCy, (float) (radius*0.8), colors, null, Shader.TileMode.REPEAT);

            //通过path绘制棱形表盘指针
            bmp = Bitmap.createBitmap(20, (int) (radius/2+60), Bitmap.Config.ARGB_8888);
            Canvas canvas = new Canvas(bmp);
            Paint paint6 = new Paint();
            paint6.setAntiAlias(true);
            paint6.setColor(getResources().getColor(R.color.orange));
            Path path = new Path();
            path.moveTo(10,0);
            path.lineTo(20,50);
            path.lineTo(10, (float) (radius/2+60));
            path.lineTo(0,50);
            path.lineTo(10,0);
            canvas.drawPath(path,paint6);
            canvas.drawBitmap(bmp, 170,10, paint6);
            canvas.save();
            canvas.restore();
        }
    }

    private int resolveMeasured(int measureSpec, int desired) {
        int specSize = MeasureSpec.getSize(measureSpec);//获取View的大小
        int result;
        switch (MeasureSpec.getMode(measureSpec)) {
            case MeasureSpec.UNSPECIFIED://不对View大小做限制,如:ListView,ScrollView
                result = desired;
                break;
            case MeasureSpec.AT_MOST://大小不可超过某数值,如:wrap_content
                result = Math.max(specSize, desired);
                break;
            case MeasureSpec.EXACTLY://确切的大小,如:100dp或者march_parent
                result = specSize;
                break;
            default:
                result = specSize;
                break;
        }
        return result;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawBG(canvas);//画背景
        drawPointerLine(canvas);//画刻度
//        drawPointer(canvas);//画指针
        drawLabelText(canvas);//写标题
        drawValText(canvas);//写当前刻度值
    }

    //画圆背景  和扇形
    private void drawBG(Canvas canvas){
        canvas.save();//用来保存canvas的状态
        bgRoundPaint.setStrokeWidth(2f);//设置描边宽度
        bgRoundPaint.setColor(bgRoundColor);//设置画笔颜色
//        bgRoundPaint.setStyle(Paint.Style.STROKE);// 描边  画的空心圆
//        bgRoundPaint.setStyle(Paint.Style.FILL);// 填充  画的实心圆
        bgRoundPaint.setStyle(Paint.Style.FILL_AND_STROKE);// 描边加填充  画的实心圆
        canvas.drawCircle(mCx,mCy,radius,bgRoundPaint);
        bgRoundPaint.setColor(bgRoundColor2);
        canvas.drawCircle(mCx,mCy, (float) (radius*0.9),bgRoundPaint);
        canvas.restore();

        canvas.save();
//        arcPaint.setColor(arcPaintColor);
        arcPaint.setShader(arcShader);//Paint 类通过setShader()方法来支持渐变
        arcPaint.setStyle(Paint.Style.FILL);
        canvas.drawArc(mArcRect, arcStartDegree, sweepAngle, true, arcPaint);
        canvas.restore();//用来恢复Canvas旋转、缩放等之后的状态,当和canvas.save( )一起使用时,恢复到canvas.save( )保存时的状态
    }

    //画扇形刻度点
    private void drawPointerLine(Canvas canvas){
        canvas.save();//保存之前的画布状态
        canvas.translate(mCx, mCy);//把当前画布的原点移到(mCx, mCy),后面的操作都以(mCx, mCy)作为参照点,默认原点为(0,0)
        canvas.rotate(arcStartDegree);
        for (int i=0; i<=valRange; i++){//这里这个valRange是最大刻度减去最小刻度,总共需要画的刻度数,可动态更改
            if (i < lowDialLimit){
                pointerPaint.setColor(colorDialLower);
            }else if (i >= lowDialLimit && i <= middleDialLimit){
                pointerPaint.setColor(colorDialMiddleLower);
            }else if (i > middleDialLimit && i<= highDialLimit){
                pointerPaint.setColor(colorDialMiddle);
            }else {
                pointerPaint.setColor(colorDialHigh);
            }
            if (i % dialStep == 0){
                //长刻度
                pointerPaint.setStrokeWidth(strokeLDial);//设置长刻度线粗度
                canvas.drawLine(radiusDial, 0, radiusDial - lengthLDial, 0, pointerPaint);

                String text = String.format("%.0f", minVal + i);
                drawPointerText(canvas, text, i);
            }else {
                //短刻度
                pointerPaint.setStrokeWidth(strokeSDial);
                int offset = (lengthLDial - lengthSDial) / 2;
                canvas.drawLine(radiusDial - offset, 0, radiusDial - lengthSDial, 0, pointerPaint);
            }
            canvas.rotate(sRoateDegree);//每画一个刻度旋转一次画布
        }
        canvas.restore();
    }

    //写大刻度数值
    private void drawPointerText(Canvas canvas, String text, int i){
        canvas.save();

        int currentCenterX = (int) (radiusDial - lengthLDial - strokeLDial - pointerPaint.measureText(String.valueOf(text)) / 2);
        canvas.translate(currentCenterX, 0);
        canvas.rotate(360 - arcStartDegree -  i * sRoateDegree);

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

        canvas.drawText(text, 0, textBaseLine, pointerPaint);
        canvas.restore();
    }

    //画指针
    private void drawPointer(Canvas canvas){
        canvas.save();
//        canvas.rotate(sRoateDegree*dialStep+18,mCx, mCy);
        canvas.rotate(row,mCx, mCy);
        pointerPaint.setColor(Color.GREEN);
        canvas.drawBitmap(bmp,mCx-10,mCy-50,pointerPaint);
        pointerPaint.setColor(Color.BLUE);
        canvas.drawCircle(mCx,mCy,6,pointerPaint);
        canvas.restore();
    }

    //画表盘底部的标题
    private void drawLabelText(Canvas canvas) {
        canvas.save();
        canvas.translate(mCx, mCy +  radius*0.66f);
        int textBaseLine = (int) (0 + (labelFontMetrics.bottom - labelFontMetrics.top) / 2 - labelFontMetrics.bottom);
        canvas.drawText(labelText, 0, textBaseLine, labelPaint);
        canvas.restore();
    }

    //在仪表盘中间写刻度值(如果写刻度值就不能画指针了)
    private void drawValText(Canvas canvas) {
        if(valText != null) {
            canvas.save();
            canvas.translate(mCx, mCy);
            int textBaseLine = (int) (0 + (valFontMetrics.bottom - valFontMetrics.top) / 2 - valFontMetrics.bottom);
            canvas.drawText(valText, 0, textBaseLine, valPaint);
            canvas.restore();
        }
    }

    /**
     * @param labelText  表盘底部的标题
     * @param formatter  中间值格式
     * @param maxVal   最大值
     * @param minVal   最小值
     * @param dialStep 刻度线 必须能被最大范围整除 (maxVal - minVal) / dialStep = 0
     * @param lowDialLimit  第一个警戒线
     * @param middleDialLimit  第二个警戒线
     * @param highDialLimit 第三个警戒线
     * @param val 当前实际刻度值
     */
    public void init(String labelText,String formatter, float maxVal, float minVal,int dialStep, int lowDialLimit, int middleDialLimit, int highDialLimit,float val){
        this.labelText = labelText;
        this.dialStep = dialStep;//大刻度线间距
        float drawDegree = (360 - (180 - arcStartDegree) * 2);
        this.maxVal = maxVal;
        this.minVal = minVal;
        this.valRange = maxVal - minVal;//一共要画的刻度值
        this.formatter = formatter;
        this.drawDialNum = (int) Math.ceil(valRange / dialStep);//要画的大刻度块数
        this.stepDegree =  drawDegree / drawDialNum;//每一大刻度间的角度
        this.lowDialLimit = (int) (lowDialLimit - minVal);
        this.middleDialLimit = (int) (middleDialLimit - minVal);
        this.highDialLimit = (int) (highDialLimit - minVal);
        this.sRoateDegree = stepDegree/dialStep; // 短刻度旋转角度(大刻度间的小刻度角度)

        //这是实际标示的值
        realVal = Math.max(Math.min(val, maxVal), minVal);
        valText = String.format(formatter, realVal);
        sweepAngle = (realVal -minVal) / valRange * (360 - (180 - arcStartDegree) * 2);

        float v = 100.0f / valRange;
        row = 2.7f*(realVal-minVal)*v+45; //计算出的旋转角度,由于前面绘制指针控件的角度是垂直向下的,表盘的起始角度是135度,所以加45度
        invalidate();
    }
}

 用法如下,内容根据实际更改:

布局:

<com.example.test1.customView.MyDashboardView
        android:id="@+id/myDashboardView"
        android:layout_width="@dimen/dimen_200dp"
        android:layout_height="@dimen/dimen_200dp"
        android:layout_gravity="center_horizontal"/>

页面里使用: 

private void initMyDashboardView(){
        myDashboardView = findViewById(R.id.myDashboardView);
        myDashboardView.init("标题","%.1f°C", 100f, -40f, 10, 0,40, 70,30);
    }

到这里自定义仪表盘就完成了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

时代新人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值