效果图
package com.mdm.dashboard; import android.animation.ObjectAnimator; import android.content.Context; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Path; import android.graphics.PointF; import android.os.Build; import android.text.TextPaint; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.widget.Toast; import java.util.ArrayList; import java.util.List; import androidx.annotation.Nullable; import androidx.annotation.RequiresApi; public class DashBoardView extends View { private Context mContext; //刻度最大值 private float maxValue = 100; //当前指针指的值 private float currentValue = 33; //单位值代表了多少度 private float cellAngle = 1; //半径 private float radius = 100; //圆心坐标 private PointF centerPoint; //边距 private float margin = 5; //仪表盘宽度 private float arcLineWidth = 20; private List<RangInfo> mRangList; private Paint dashBoardPaint; private Paint dashLinePaint; private Paint pointerPaint; //刻度文字 private TextPaint dashLineTextPaint; //底部标题文字 private TextPaint dashTitleTextPaint; private int titleColor; //标题 private String title = "回报率"; //单位 private String unit = "%"; private Path path; public DashBoardView(Context context) { super(context); init(context); } public DashBoardView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context); } public DashBoardView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context); } /** * 初始化 * @param context */ private void init(Context context) { dashBoardPaint = new Paint(Paint.ANTI_ALIAS_FLAG); dashBoardPaint.setStyle(Paint.Style.STROKE); dashBoardPaint.setStrokeWidth(arcLineWidth); dashLineTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); dashLineTextPaint.setStyle(Paint.Style.FILL); dashLineTextPaint.setStrokeWidth(2); dashLineTextPaint.setTextSize(sp2px(20)); dashTitleTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG); dashTitleTextPaint.setStyle(Paint.Style.FILL); dashTitleTextPaint.setStrokeWidth(2); dashTitleTextPaint.setTextSize(sp2px(20)); pointerPaint = new Paint(Paint.ANTI_ALIAS_FLAG); pointerPaint.setStyle(Paint.Style.FILL); // pointerPaint.setColor(); dashLinePaint = new Paint(Paint.ANTI_ALIAS_FLAG); dashLinePaint.setStrokeWidth(2); dashLinePaint.setColor(Color.WHITE); dashLinePaint.setStrokeCap(Paint.Cap.ROUND); centerPoint = new PointF(); path = new Path(); mContext = context; setRanValue(new ArrayList<RangInfo>()); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int height = getMeasuredHeight(); int width = getMeasuredWidth(); if(height > width){ margin = width / 20f; width -= (margin * 2) + arcLineWidth*2; // radius = ((width / 6f)*5f)/2f; radius = width / 2f; }else{ margin = height / 20f; height -= (margin * 2) + arcLineWidth*2; // radius = ((height / 6f)*5f)/2f; radius = height/2f; } centerPoint.set(getMeasuredWidth()/2, getMeasuredHeight()/2); } @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) @Override protected void onDraw(Canvas canvas) { drawDashBoard(canvas); drawLine(canvas); drawPointer(canvas);//画指针 drawTitle(canvas); } /** * 绘制仪表盘 * @param canvas */ @RequiresApi(api = Build.VERSION_CODES.LOLLIPOP) private void drawDashBoard(Canvas canvas){ if(mRangList != null) { float startAngle = 135; for (RangInfo info : mRangList) { dashBoardPaint.setColor(info.getColor()); //记录下仪表盘的颜色范围区间用于判断 PointF pointF = new PointF(startAngle - 135,(startAngle + info.getValue() * cellAngle)-135); info.setPointF(pointF); //画弧线 // canvas.drawArc(centerPoint.x - radius,margin + arcLineWidth,centerPoint.x + radius,margin + arcLineWidth + 2 * radius,startAngle,info.getValue() * cellAngle,false,dashBoardPaint); canvas.drawArc(centerPoint.x - radius,centerPoint.y - radius,centerPoint.x + radius,centerPoint.y + radius,startAngle,info.getValue() * cellAngle,false,dashBoardPaint); startAngle += info.getValue() * cellAngle; } } } /** * 画刻度 * @param canvas */ private void drawLine(Canvas canvas){ canvas.save(); canvas.translate(centerPoint.x,centerPoint.y); canvas.rotate(135); for (int i = 0; i <= 270;){ if(i % 45 == 0){ drawLineText(canvas,i); canvas.drawLine(radius - arcLineWidth*4/3,0,radius + arcLineWidth/2,0,dashLinePaint); } else canvas.drawLine(radius - arcLineWidth/2,0,radius + arcLineWidth/2,0,dashLinePaint); canvas.rotate(9); i+=9; } canvas.restore(); } /** * 画刻度值 */ private void drawLineText(Canvas canvas,float angle){ float value = angle / cellAngle; canvas.save(); drawCenterText(value,angle,canvas); canvas.restore(); } /** * 获取圆上的点的坐标 * @return * 这里的圆心坐标为什么是0,0是因为canvas已经移到圆的圆心的位置,那么新的坐标轴中的圆心点就是0,0 */ private PointF getRoundXY(float angle,float radius){ PointF xyPoint = new PointF(); xyPoint.x = (float) (0 + radius * Math.cos(angle * Math.PI/180)); xyPoint.y = (float) (0 + radius * Math.sin(angle * Math.PI/180)); return xyPoint; } /** * 居中显示文字 * @param value * @param canvas */ private void drawCenterText( float value,float angle,Canvas canvas){ TextPaint.FontMetrics fontMetrics = dashLineTextPaint.getFontMetrics(); dashLineTextPaint.setTextAlign(Paint.Align.CENTER); int textBaseLine = (int) (0 + (fontMetrics.bottom - fontMetrics.top) /2 - fontMetrics.bottom); // PointF pointF = getRoundXY(0,radius - arcLineWidth*2.5f); PointF pointF = getRoundXY(0, radius - arcLineWidth*4/3 - dashLineTextPaint.measureText(String.valueOf(Math.round(value)))/2); canvas.translate(pointF.x,pointF.y); canvas.rotate( -135-angle); dashLineTextPaint.setColor(getPointerTextColor(angle)); canvas.drawText(String.valueOf(Math.round(value)), 0, textBaseLine, dashLineTextPaint); // canvas.drawText(listener != null?listener.getFormatValue(value):String.valueOf(Math.round(value)), 0, textBaseLine, dashLineTextPaint); } /** * 画指针 * @param canvas */ private void drawPointer(Canvas canvas){ pointerPaint.setColor(getPointerTextColor(currentValue *cellAngle)); canvas.drawCircle(centerPoint.x,centerPoint.y,3,pointerPaint); canvas.save(); canvas.translate(centerPoint.x,centerPoint.y); canvas.rotate(135 + currentValue *cellAngle); path.reset(); path.moveTo(0,0); path.lineTo(radius * 1/10,-radius * 1/15); path.lineTo(radius * 3/4,0); path.lineTo(radius * 1/10,radius * 1/15); path.close(); canvas.drawPath(path,pointerPaint); canvas.restore(); } /** * 画title * @param canvas */ private void drawTitle(Canvas canvas){ if(titleColor != 0) dashTitleTextPaint.setColor(titleColor); TextPaint.FontMetrics fm = dashTitleTextPaint.getFontMetrics(); float textHeight = fm.bottom - fm.top; dashTitleTextPaint.setTextAlign(Paint.Align.CENTER); canvas.drawText(title, centerPoint.x, centerPoint.y + radius * 4/5 + textHeight, dashTitleTextPaint); dashTitleTextPaint.setColor(getPointerTextColor(currentValue * cellAngle)); canvas.drawText((listener != null?listener.getFormatValue(currentValue):Math.round(currentValue)) + unit, centerPoint.x, centerPoint.y + radius * 4/5, dashTitleTextPaint); } /** * 指针和文字的颜色 * @param angle 度数 减去135后的度数 * @return 获取某个区域的文字的颜色和指针的颜色 */ private int getPointerTextColor(float angle){ for (RangInfo info : mRangList){ if(angle >= info.getPointF().x && angle <= info.getPointF().y){ return info.getColor(); } } return 0xff000000; } /** * 设置底部标题文字 * @param title * @param unit * @return */ public DashBoardView setTitle(String title, String unit){ if(title == null) title = ""; if(unit == null) unit = ""; this.title = title; this.unit = unit; postInvalidate(); return this; } /** * 设置title文字大小 * @return */ public DashBoardView setTitleSize(float textSize){ if(textSize <= 0) textSize = 15; dashTitleTextPaint.setTextSize(sp2px(textSize)); postInvalidate(); return this; } /** * 设置title颜色 * @param textColor * @return */ public DashBoardView setTitleColor(int textColor){ try{ dashTitleTextPaint.setColor(textColor); }catch (Exception e){ textColor = 0xff000000; dashTitleTextPaint.setColor(textColor); } titleColor = textColor; postInvalidate(); return this; } /** * 设置区间值和颜色 * @param rangInfos * @return */ public DashBoardView setRanValue(List<RangInfo> rangInfos){ if(rangInfos == null || rangInfos.size() == 0){ mRangList = getDefaultRangList(); return this; } float sum = 0; for(RangInfo info : rangInfos){ if(info == null)continue; sum += info.getValue() < 0?0:info.getValue(); } if(sum > 0){ maxValue = sum; mRangList = rangInfos; cellAngle = 270 / maxValue; }else{ mRangList = getDefaultRangList(); } postInvalidate(); return this; } /** * 便捷设置 * @param ranValues * @return */ public DashBoardView setRanValue(RangInfo... ranValues){ if(ranValues == null || ranValues.length == 0)return this; List<RangInfo> rangInfos = new ArrayList<>(); for (RangInfo rangInfo : ranValues){ rangInfos.add(rangInfo); } setRanValue(rangInfos); return this; } /** * 获取当前指向的值 * @return */ public float getCurrentValue() { return currentValue; } /** * 用来做动画的设置当前指向的值 * @param currentValue */ private void setCurrentValue(float currentValue) { this.currentValue = currentValue; postInvalidate(); } /** * 设置当前值 * @param value * @param isAnim true 需要动画 false 不要动画 */ public void setValue(float value,boolean isAnim,IValueFormat listener){ if(value < 0 || value > maxValue){ Toast.makeText(mContext,"设置无效!设置的值大于当前最大值",Toast.LENGTH_LONG).show(); return; } if(isAnim) ObjectAnimator.ofFloat(this,"currentValue",0,value).setDuration(2000).start(); else setCurrentValue(value); setListener(listener); } /** * 设置当前值 * @param value * @param isAnim true 需要动画 false 不要动画 */ public void setValue(float value,boolean isAnim){ setValue(value,isAnim,null); } /** * 设置刻度线的颜色 * @param lineColor */ public DashBoardView setLineColor(int lineColor){ if(dashLinePaint != null) { try { dashLinePaint.setColor(lineColor); }catch (Exception e){ dashLinePaint.setColor(Color.WHITE); } } postInvalidate(); return this; } /** * 设置刻度线的宽度 * @param lineWidth */ public DashBoardView setLineWidth(float lineWidth){ if(lineWidth < 0)return this; dashLinePaint.setStrokeWidth(lineWidth); postInvalidate(); return this; } /** * 设置刻度的文字大小 */ public DashBoardView setLineTextSize(float textSize){ if(textSize < 0)return this; dashLineTextPaint.setTextSize(sp2px(textSize)); postInvalidate(); return this; } /** * 设置底部标题的文字大小 * @param textSize * @return */ public DashBoardView setTitleTextSize(float textSize){ if(textSize < 0)return this; dashTitleTextPaint.setTextSize(sp2px(textSize)); postInvalidate(); return this; } /** * 设置圆环的线宽度 * @param circleLineWidth * @return */ public DashBoardView setDashBoardCircleLineWidth(float circleLineWidth){ if(circleLineWidth < 0)return this; arcLineWidth = circleLineWidth; dashBoardPaint.setStrokeWidth(arcLineWidth); postInvalidate(); return this; } /** * 获取默认的区间显示和总数 * @return */ private List<RangInfo> getDefaultRangList(){ return getDefaultRangList(100f); } public List<RangInfo> getDefaultRangList(float maxValue){ this.maxValue = maxValue; cellAngle = 270 / this.maxValue; List<RangInfo> list = new ArrayList<>(); for (int i = 0;i < 4;i++){ RangInfo info = new RangInfo(); if(i == 0)info.setColor(0xffE13020); else if(i == 1) info.setColor(0xffD7A52D); else if(i == 2) info.setColor(0xff4089D7); else info.setColor(0xff30AD37); info.setValue(this.maxValue/4); list.add(info); } return list; } /** * dp转px * @param dpVal * @return */ protected float dp2px(float dpVal) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); } /** * sp转px * @param spVal * @return */ protected float sp2px(float spVal) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, spVal, getResources().getDisplayMetrics()); } /** * 设置区间范围 */ public static class RangInfo{ private float value; private int color; //记录颜色角度的区间并不是坐标 private PointF pointF; public RangInfo() {} public RangInfo(float value, int color) { this.value = value; this.color = color; } public float getValue() { return value; } public void setValue(float value) { this.value = value; } public int getColor() { return color; } public void setColor(int color) { this.color = color; } public PointF getPointF() { return pointF; } public void setPointF(PointF pointF) { this.pointF = pointF; } } /** * 取值处理 */ public interface IValueFormat{ String getFormatValue(float value); } private IValueFormat listener; public DashBoardView setListener(IValueFormat listener){ this.listener = listener; return this; } }
使用方法:
final DashBoardView dashBoardView = findViewById(R.id.dashBoard); findViewById(R.id.btnTest).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { dashBoardView.setRanValue(dashBoardView.getDefaultRangList(250f)) .setLineTextSize(10) .setLineWidth(1f) .setDashBoardCircleLineWidth(5) .setTitleColor(Color.WHITE) .setTitleSize(10) .setTitle("装逼率","%") .setValue(100f, true, new DashBoardView.IValueFormat() { @Override public String getFormatValue(float value) { DecimalFormat df=new DecimalFormat("0.0"); return df.format(value); } }); } });