最终效果
仪表盘演示
代码实现
package com.example.chartdemo;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import androidx.annotation.Nullable;
/**
* 仪表View
*/
public class MeterChartView extends View {
public MeterChartView(Context context) {
this(context,null);
}
public MeterChartView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public MeterChartView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private int widthMode;
private int heightMode;
private int widthSize;
private int heightSize;
private Paint linePaint;
private Paint fullPaint;
private Paint textPaint;
private float cX;
private float cY;
private float RADIUS;
private float dataMax=240;//最大值
private float sectionNumber=6;//区间个数
private float scaleNumber=24;//刻度个数
private float data=0;//当前数据值
private String unit="";
private float refreshData=0;
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
widthMode = MeasureSpec.getMode(widthMeasureSpec);
heightMode = MeasureSpec.getMode(heightMeasureSpec);
widthSize = MeasureSpec.getSize(widthMeasureSpec);
heightSize = MeasureSpec.getSize(heightMeasureSpec);
cX=widthSize/2f;
cY=heightSize/2f;
if (cY<cX){
RADIUS=cY-20;
}else {
RADIUS=cX-20;
}
linePaint=new Paint();
linePaint.setAntiAlias(true);//消除锯齿
linePaint.setColor(Color.WHITE);
linePaint.setStrokeWidth(4);
linePaint.setStyle(Paint.Style.STROKE);//决定样式,填充还是画线
fullPaint=new Paint();
fullPaint.setAntiAlias(true);//消除锯齿
fullPaint.setStrokeWidth(4);
textPaint=new Paint();
textPaint.setAntiAlias(true);//消除锯齿
textPaint.setTextSize(30);
textPaint.setColor(Color.WHITE);
textPaint.setTextAlign(Paint.Align.CENTER);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//刷屏幕扇形特效
RectF mRectRefresh = new RectF(cX - RADIUS, cY-RADIUS, cX + RADIUS, cY + RADIUS);
fullPaint.setColor(Color.RED);
float refreshStep=240/dataMax;
canvas.drawArc(mRectRefresh, 150, refreshData*refreshStep, true, fullPaint);
//绘制背景黑色扇形
fullPaint.setColor(Color.BLACK);
canvas.drawCircle(cX,cY,RADIUS-20,fullPaint);
//绘制扇形
linePaint.setColor(Color.WHITE);
RectF mRectF = new RectF(cX - RADIUS, cY-RADIUS, cX + RADIUS, cY + RADIUS);
canvas.drawArc(mRectF, 150, 240, true, linePaint);
//绘制遮罩扇形
fullPaint.setColor(Color.BLACK);
canvas.drawCircle(cX,cY,RADIUS-50,fullPaint);
//绘制细线圆
linePaint.setStrokeWidth(2);
canvas.drawCircle(cX,cY,RADIUS-20,linePaint);
float sectionAngle=240/sectionNumber;//每个大区间的角度值
float sectionStep=dataMax/sectionNumber;
for (int k=0;k<sectionNumber+1;k++){
float keDuStartX = cX + RADIUS * (float) Math.cos((150+(sectionAngle*k)) / 180 * Math.PI);
float keDuStartY = cY + RADIUS * (float) Math.sin((150+(sectionAngle*k)) / 180 * Math.PI);
float keDuEndX = cX + (RADIUS-50) * (float) Math.cos((150+(sectionAngle*k)) / 180 * Math.PI);
float keDuEndY = cY + (RADIUS-50) * (float) Math.sin((150+(sectionAngle*k)) / 180 * Math.PI);
//绘制区间刻度线
linePaint.setStrokeWidth(4);
canvas.drawLine(keDuStartX,keDuStartY,keDuEndX,keDuEndY,linePaint);
int textValue= (int) (k*sectionStep);
//绘制刻度值
if (keDuEndX<cX){
//左侧刻度
textPaint.setColor(Color.parseColor("#FF9800"));
textPaint.setTextSize(30);
canvas.drawText(textValue+"",keDuEndX+30,keDuEndY+10,textPaint);
}else if (keDuEndX==cX){
//中间刻度
textPaint.setColor(Color.parseColor("#FF9800"));
textPaint.setTextSize(30);
canvas.drawText(textValue+"",keDuEndX,keDuEndY+30,textPaint);
}else {
//右侧刻度
textPaint.setColor(Color.parseColor("#FF9800"));
textPaint.setTextSize(30);
canvas.drawText(textValue+"",keDuEndX-30,keDuEndY+10,textPaint);
}
}
float scaleAngle=240/scaleNumber;//每个刻度的角度值
for (int k=0;k<scaleNumber;k++){
float keDuStartX = cX + RADIUS * (float) Math.cos((150+(scaleAngle*k)) / 180 * Math.PI);
float keDuStartY = cY + RADIUS * (float) Math.sin((150+(scaleAngle*k)) / 180 * Math.PI);
float keDuEndX = cX + (RADIUS-30) * (float) Math.cos((150+(scaleAngle*k)) / 180 * Math.PI);
float keDuEndY = cY + (RADIUS-30) * (float) Math.sin((150+(scaleAngle*k)) / 180 * Math.PI);
//绘制细刻度线
linePaint.setStrokeWidth(2);
canvas.drawLine(keDuStartX,keDuStartY,keDuEndX,keDuEndY,linePaint);
}
//绘制指针
float dataStep=240/dataMax;
float pointerStartX = cX;
float pointerStartY = cY;
float pointerEndX = cX + (RADIUS-40) * (float) Math.cos((150+(data*dataStep)) / 180 * Math.PI);
float pointerEndY = cY + (RADIUS-40) * (float) Math.sin((150+(data*dataStep)) / 180 * Math.PI);
linePaint.setStrokeWidth(6);
linePaint.setColor(Color.RED);
canvas.drawLine(pointerStartX,pointerStartY,pointerEndX,pointerEndY,linePaint);
fullPaint.setColor(Color.RED);
canvas.drawCircle(cX,cY,20,fullPaint);
//绘制数据值
int nowData= (int) data;
textPaint.setColor(Color.WHITE);
textPaint.setTextSize(40);
canvas.drawText(nowData+unit,cX,cY+(RADIUS/2f),textPaint);
}
/**
* 初始化轴数据
* @param dataMax
* @param sectionNumber
* @param scaleNumber
*/
public void initConstant(int dataMax,int sectionNumber,int scaleNumber){
this.dataMax=dataMax;
this.sectionNumber=sectionNumber;
this.scaleNumber=scaleNumber;
postInvalidate();
}
/**
* 设置数据
* @param dataValue 数据
* @param unit 单位
*/
public void setData(int dataValue,String unit){
data=dataValue;
this.unit=unit;
postInvalidate();
}
/**
* 刷屏幕特效
* @param data
*/
public void refreshUi(int data){
refreshData=data;
postInvalidate();
}
}
使用方法
- 在xml中引用
<LinearLayout
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:ignore="MissingConstraints">
<com.example.chartdemo.MeterChartView
android:id="@+id/left"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="600px"/>
<com.example.chartdemo.MeterChartView
android:id="@+id/right"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="600px"/>
</LinearLayout>
- 初始化参数与数据
package com.example.chartdemo;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import com.example.chartdemo.util.ActionCallback;
import com.example.chartdemo.util.BarHeartbeat;
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initMeter();
}
private MeterChartView left;
private MeterChartView right;
private void initMeter() {
left=(MeterChartView) findViewById(R.id.left);
left.initConstant(240,6,24);
right=(MeterChartView) findViewById(R.id.right);
right.initConstant(8000,8,40);
reSetLeftMeter(0,240);
reSetRightMeter(0,80);
}
/**
* 重置仪表
* @param realData 真实数据值
* @param maxData 模拟旋转最大值
*/
public void reSetLeftMeter(int realData, int maxData){
new BarHeartbeat().start(4, realData, maxData, new ActionCallback() {
@Override
public void toDo(Object o) {
left.setData((int)o,"km/h");
}
});
new BarHeartbeat().start(4, 0, maxData, new ActionCallback() {
@Override
public void toDo(Object o) {
left.refreshUi((int)o);
}
});
}
/**
* 重置仪表
* @param realData 真实数据值
* @param maxData 模拟旋转最大值
*/
private void reSetRightMeter(int realData, int maxData){
new BarHeartbeat().start(10, realData, maxData, new ActionCallback() {
@Override
public void toDo(Object o) {
right.setData((int)o*100,"r/min");
}
});
new BarHeartbeat().start(10, 0, maxData, new ActionCallback() {
@Override
public void toDo(Object o) {
right.refreshUi((int)o*100);
}
});
}
}
辅助工具类
package com.example.chartdemo.util;
import android.os.Looper;
import android.os.Message;
import java.util.ArrayList;
import java.util.List;
/**
* 心跳计时器
*/
public class BarHeartbeat {
//事件标签
public final int ACTION_TAG =0xFF;
//要回调的接口
private ActionCallback thisActionDo;
//Handler事件监听器
private android.os.Handler mHandler = new android.os.Handler(Looper.getMainLooper()){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
switch (msg.what){
case ACTION_TAG:
thisActionDo.toDo(msg.obj);
if ((int)msg.obj==maxValue && forTag==true){
doHUi();
}
break;
}
}
};
int dataValue;
int maxValue;
int countDownTime;
public void start(int countDownTime,int dataValue,int maxValue,ActionCallback actionDo){
this.dataValue=dataValue;
this.maxValue=maxValue;
this.countDownTime=countDownTime;
thisActionDo=actionDo;
for (int a=0;a<maxValue;a++){
Message msg = mHandler.obtainMessage();
msg.what = ACTION_TAG;
msg.obj=a+1;
mHandler.sendMessageDelayed(msg,countDownTime*a);
}
};
private boolean forTag=true;
private void doHUi(){
forTag=false;
for (int k=0;k<=maxValue-dataValue;k++){
Message msg2 = mHandler.obtainMessage();
msg2.what = ACTION_TAG;
msg2.obj=maxValue-k;
mHandler.sendMessageDelayed(msg2,countDownTime*k);
}
}
}
//回调接口
public interface ActionCallback {
void toDo(Object o);
}