自定义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);
}
到这里自定义仪表盘就完成了。