自定义view之弧形进度条

2021-08-05  本文已影响0人  重新起步

车载需要做一个suv后门的开关演示器,用户可以通过在屏幕上触摸来控制车门的开关角度;所以我们需要做一个类似与扇形的进度条;

需要具备的基础知识如下:
画点:
画图片:
画线:canvas.drawLine(startX, startY, endX, endY, linePaint);
画扇形:https://blog.csdn.net/ls15256928597/article/details/70077825
填充色渐变:https://blog.csdn.net/harvic880925/

首先我们考虑最简单的场景:即车门最大只能打开90度;如下图:


p点是用户touch的区域;
q点是显示在进度刻度上的点,根据p点的变化来变化;
oq是需要展示的游标;
正方形是实际view的大小;
那么我们需要做的是,

view的绘制,大致思路如下

步骤1.绘制蓝色扇形,

1.知道圆点,知道半经,先画一个正方形,正方形的中心就是圆点,正方形边长的一半,就是半径;
2.扇形是从,90度,画到180度的;

步骤2.绘制q点

1.需要知道q点对应的x,y
2.x和y可以根据半斤和角度a通过勾股定理计算出来
3.角度a根据用户点击p对应的x和y值,然后根据勾股定理计算出来

步骤3.绘制线段

步骤2中计算出q点的x和y,o点的x,y是已知的,连接起来即可;

所以ondraw方法,分为3步:


那么p点如何知道,那就要通过ontouch方法,事件的x,y对应的就是p点的x和y,代码如下:
然后通过勾股定理,计算出角度a;
最后调用 invalidate(),通知重绘,也就是每move一次,界面重绘一次;

package com.ford.sync.fordcalendar.ui.view;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.SweepGradient;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import com.ford.sync.fordcalendar.R;

/**
 * author: lwang253 date: 2021/1/25 create CalendarApplication.
 */
public class ArcProgress extends View {

    private int max = 90;// 支持的最大角度

    private Paint arcPaint; // 弧形画笔

    private Paint linePaint; // 直线画笔

    private Paint pointPaint; // 点画笔

    private int radio;

    private int whsize; //

    private double angle; // 当前的角度

    private float touchX; // 触摸x
    private float touchY; // 触摸y

    private float pointX; // 刻度x
    private float pointY; // 刻度y

    public ArcProgress(Context context) {
        super(context);
        init();
    }

    public ArcProgress(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

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

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

    private void init() {
        arcPaint = new Paint();
        arcPaint.setStyle(Paint.Style.FILL);
        arcPaint.setStrokeWidth(1);
        arcPaint.setTextSize(30);

        linePaint = new Paint();
        linePaint.setStyle(Paint.Style.FILL);
        linePaint.setStrokeWidth(3);
        linePaint.setColor(Color.BLACK);

        pointPaint = new Paint();
        pointPaint.setAntiAlias(true);
        pointPaint.setTextSize(36);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
//        canvas.drawColor(Color.RED);// 实际可以去掉,用于观察view的实际大小
        calculatePointByAngle(angle);
        drawArc(canvas);
        drawLine(canvas);
        drawPoint(canvas);
    }

    /**
     * 根据当前的角度,找到刻度q点的位置
     *
     * @param angle
     */
    private void calculatePointByAngle(double angle) {
        Log.d("ArcProgress", "sin30:" + Math.sin(30 * Math.PI / 180));
        pointX = radio - (float) (radio * Math.sin(angle * Math.PI / 180));
        pointY = radio + (float) (radio * Math.cos(angle * Math.PI / 180));
        Log.d("ArcProgress", "calculatePointByAngle pointX:" + pointX + ";pointY:" + pointY);
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        whsize = w > h ? h : w;
        radio = whsize / 2;
        super.onSizeChanged(w, h, oldw, oldh);
    }

    private void drawPoint(Canvas canvas) {

        Bitmap bmp = BitmapFactory.decodeResource(getResources(), R.drawable.radio_sel);
        Rect mSrcRect = new Rect(0, 0, bmp.getWidth(), bmp.getHeight());

        int left = (int) (pointX - bmp.getWidth()/2);
        int top = (int) (pointY - bmp.getHeight()/2);
        Rect mDestRect = new Rect(left, top, left+bmp.getWidth(), top+bmp.getHeight());
        canvas.drawBitmap(bmp, mSrcRect, mDestRect, pointPaint);
    }

    private void drawLine(Canvas canvas) {
        canvas.drawLine(radio, radio, pointX, pointY, linePaint);
    }

    private void drawArc(Canvas canvas) {
        // 矩形区域,这种方式画出来的扇形占view的1/4
        RectF oval = new RectF(0, 0, whsize, whsize);
        // 渐变的填充色
        Shader gradient = new RadialGradient(radio, radio,
                radio, Color.TRANSPARENT, Color.BLUE, Shader.TileMode.REPEAT);
        arcPaint.setShader(gradient);
        canvas.drawArc(oval, 90, 90, true, arcPaint);
    }

    public float Dp2Px(Context context, float dp) {
        final float scale = context.getResources().getDisplayMetrics().density;
        return (dp * scale + 0.5f);
    }


    /**
     * 根据触摸点,计算角度
     *
     * @param event
     * @return
     */
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                touchX = event.getX();
                touchY = event.getY();
                Log.d("ArcProgress", "x:" + touchX + ";  y:" + touchY);
                if (touchX > radio || touchY < radio) {
                    // 在另外三个区域不处理touch事件;
                    return true;
                }
                calculateAngle();
                Log.d("ArcProgress", "angle:" + angle);
                invalidate();// 计算出角度后,刷新界面
                break;
        }
        return true;
    }

    /**
     * 三角函数计算角度a Math.atan计算的是弧度
     */
    private void calculateAngle() {
        angle = Math.atan((radio - touchX) / (touchY - radio)) * 180 / Math.PI;
        if (angle > 90) {
            angle = 90;
        }
        if (angle < 0) {
            angle = 0;
        }

    }
};

上一篇下一篇

猜你喜欢

热点阅读