Android自定义View:简单实现汽车仪表盘

1,098 阅读2分钟
public class CarSpeedDashboardView extends View {

    private Paint mPaintOutDashboard;//外部码数表圆弧的画笔
    private Paint mPaintSpeedPoint;//码数表指针的画笔
    private Paint mPaintSpeedScale;//码数表上刻度画笔
    private Paint mPaintSpeedScaleText;//码数表上刻度文字画笔
    private Paint mPaintSpeedText;//实时码数表对应数值画笔
    private int outDashBoardWidth = 40;//外部码数表圆弧的宽度


    private float rotateValue = 50;//动画需要改变的圆弧角度
    private float DEFAULT_START_ANGLE = 50;//默认需要旋转的角度,canvas移动到中心时  需要默认旋转(360-260)/2

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

    public CarSpeedDashboardView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

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

    /**
     * 初始化参数
     */
    private void init() {
        mPaintOutDashboard = new Paint();
        mPaintOutDashboard.setStrokeWidth(outDashBoardWidth);
        mPaintOutDashboard.setStyle(Paint.Style.STROKE);
        mPaintOutDashboard.setTextAlign(Paint.Align.CENTER);
        mPaintOutDashboard.setAntiAlias(true);
        mPaintOutDashboard.setStrokeCap(Paint.Cap.ROUND);


        mPaintSpeedPoint = new Paint();
        mPaintSpeedPoint.setColor(Color.BLACK);
        mPaintSpeedPoint.setStrokeWidth(20);
        mPaintSpeedPoint.setAntiAlias(true);
        mPaintSpeedPoint.setStyle(Paint.Style.STROKE);
        mPaintSpeedPoint.setTextAlign(Paint.Align.CENTER);
        mPaintSpeedPoint.setStrokeCap(Paint.Cap.ROUND);


        mPaintSpeedScale = new Paint();
        mPaintSpeedScale.setColor(Color.BLUE);
        mPaintSpeedScale.setStrokeWidth(5);
        mPaintSpeedScale.setAntiAlias(true);
        mPaintSpeedScale.setStyle(Paint.Style.STROKE);

        mPaintSpeedScaleText = new Paint();
        mPaintSpeedScaleText.setColor(Color.BLACK);
        mPaintSpeedScaleText.setStrokeWidth(3);
        mPaintSpeedScaleText.setTextSize(26);
        ;
        mPaintSpeedScaleText.setAntiAlias(true);

        mPaintSpeedText = new Paint();
        mPaintSpeedText.setColor(Color.BLACK);
        mPaintSpeedText.setTextSize(48);
        ;
        mPaintSpeedText.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
        mPaintSpeedText.setAntiAlias(true);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = measureSpec(widthMeasureSpec);
        int height = measureSpec(heightMeasureSpec);
        setMeasuredDimension(Math.min(width,height), Math.min(width,height));

        //测量完成  才能得到对应的宽高信息  设置渐变色
        LinearGradient sweepGradient = new LinearGradient(0
                , getMeasuredHeight(), getMeasuredWidth()
                , getMeasuredHeight()
                , Color.BLUE, Color.RED, Shader.TileMode.CLAMP);
        mPaintOutDashboard.setShader(sweepGradient);

    }

    private int measureSpec(int measureSpec) {
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
        int defaultWidth = 400;
        switch (specMode) {

            case MeasureSpec.EXACTLY:

                defaultWidth = specSize;
                break;
            case MeasureSpec.AT_MOST:
            case MeasureSpec.UNSPECIFIED:
                defaultWidth = 400;
                break;
        }
        return defaultWidth;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //绘制外部圆环
        canvas.drawArc(outDashBoardWidth / 2, outDashBoardWidth / 2, Math.min(getMeasuredWidth(), getMeasuredHeight()) - outDashBoardWidth / 2, Math.min(getMeasuredWidth(), getMeasuredHeight()) - outDashBoardWidth / 2, 140, 260, false, mPaintOutDashboard);

        drawSpeedScale(canvas);

        //绘制进度条内的进度展示
        canvas.drawArc(outDashBoardWidth / 2, outDashBoardWidth / 2, Math.min(getMeasuredWidth(), getMeasuredHeight()) - outDashBoardWidth / 2, Math.min(getMeasuredWidth(), getMeasuredHeight()) - outDashBoardWidth / 2, 140, rotateValue - DEFAULT_START_ANGLE, false, mPaintSpeedPoint);

        drawSpeedPoint(canvas);
        drawCenterSpeedSize(canvas);
    }


    /**
     * 绘制刻度和对应刻度的码数值
     *
     * @param canvas
     */
    private void drawSpeedScale(Canvas canvas) {
        canvas.save();
        float sweepAngle = 260;
        float a = sweepAngle / 26;
        canvas.translate(Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2);

        canvas.rotate(DEFAULT_START_ANGLE);

        for (int i = 0; i <= 26; i++) {
            if (i == 0 || i == 26 || i % 5 == 0) {
                canvas.drawLine(0, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - outDashBoardWidth, 0, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - 80, mPaintSpeedScale);
            } else {
                canvas.drawLine(0, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - outDashBoardWidth, 0, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - 60, mPaintSpeedScale);
            }
            String text = i * 10 + "";


            Rect rect = new Rect();
            String msg = "999";
            mPaintSpeedScaleText.getTextBounds(msg, 0, msg.length(), rect);
//            canvas.drawText(text, 0, text.length(),0, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - 85, mPaintSpeedScaleText);
//            canvas.rotate(a);


            canvas.rotate(-90);
            canvas.translate(-1 * (Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - outDashBoardWidth - 60-rect.width()/2), 0);
            canvas.rotate(90 - DEFAULT_START_ANGLE - a * i);
            canvas.drawText(text, 0, text.length(), 0, 0, mPaintSpeedScaleText);
            canvas.rotate(-1 * (90 - DEFAULT_START_ANGLE - a * i));
            canvas.translate(Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - outDashBoardWidth - 60-rect.width()/2, 0);
            canvas.rotate(90);

            canvas.rotate(a);
//
        }
        canvas.restore();

    }

    /**
     * 绘制码数表的指针
     *
     * @param canvas
     */
    private void drawSpeedPoint(Canvas canvas) {
        canvas.drawPoint(Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2, mPaintSpeedPoint);

        canvas.save();
        canvas.translate(Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2);
        canvas.rotate(rotateValue);
        canvas.drawLine(0, Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - outDashBoardWidth * 4, 0, 0, mPaintSpeedPoint);
        canvas.restore();

    }

    /**
     * 绘制对应的码数表的数值
     *
     * @param canvas
     */
    private void drawCenterSpeedSize(Canvas canvas) {
        String value = Math.round(rotateValue - 50) + "";
        Rect rect = new Rect();
        mPaintSpeedText.getTextBounds(value, 0, value.length(), rect);

        canvas.drawText(value, 0, value.length(), Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - rect.width() / 2
                , Math.min(getMeasuredWidth(), getMeasuredHeight()) / 2 - 40, mPaintSpeedText);

    }

    /**
     *
     */
    public void startAnimation() {
        if (rotateValue != 50) {
            return;
        }
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(50, 310);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                rotateValue = (float) animation.getAnimatedValue();
                if (rotateValue == 310) {
                    releaseAnimation();
                }
                invalidate();
            }
        });
        valueAnimator.start();
    }

    /**
     *
     */
    public void releaseAnimation() {
        if (rotateValue == 50) {
            return;
        }
        ValueAnimator valueAnimator = ValueAnimator.ofFloat(50, 310);
        valueAnimator.setDuration(3000);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                rotateValue = 360 - (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        valueAnimator.start();
    }

}