自定义仪表盘

效果图

 

 

 

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);
                            }
                        });
            }
        });

 

posted @ 2019-11-26 18:06  一只呆萌的萌呆  阅读(679)  评论(0编辑  收藏  举报