Android自定义View : 绘制汽车仪表盘,圆盘

Android自定义View : 绘制汽车仪表盘,圆盘,第1张

1:汽车仪表盘

2 :仪表盘分解

如图汽车仪表盘可以分解为:绘制大半圆弧,绘制刻度,绘制指针

2.1 :整个外仪表盘 DashBoardView,这个是布局文件自定义的View,我们只需要定义大弧的缺口和半径即可 2.2 :绘制刻度:刻度实际就是一个个很小的小矩形,每个刻度就是一个矩形,每一个刻度都有自己单独的坐标,具体实现就是

            先绘制小矩形,并加入到 path路径中 

            然后根据目标刻度的数量和圆弧长度,得到每个刻度之间距离

          圆弧长度  PathMeasure .

2.3 :绘制指针:重点是在绘制指针直线时,需要知道起点和终点坐标,起点坐标好说,就是仪表盘圆弧的中心点,终点坐标,需要根据刻度求解出角度,然后通过正弦或者余弦函数计算出坐标。 3:代码示例 3.1 :自定义仪表盘 DashBoardView
DashBoardView.java

public DashBoardView extends View {

    ........
    
    //  1: 定义大弧的缺口角度和半径
    private static final int DASH_BOARD_ANGLT = 120 ; 
   
    // 2: 定义仪表板的半径
    private static final int DASH_BOARD_DADIUS = Utils.dp2px(150);
    public void onDraw(Canvas canvas) {
        
        super.onDraw(canvas);

        // 3: 画弧线
        // 3.1 先确定在页面的位置 -----比如在页面中心,可以通过下面的矩形确定位置
       //  Reacf reacf = new Reacf (left,top,right,bottom);
           Reacf arcReacf = new Reacf (getWidth()/2 - RADIUS , getHeight()/2-RADIUS,
              getWidth()/2 + RADIUS , getHeight()/2 + RADIUS);
       canvas.drawArc (reacf, 90+ ANGLE/2 , 360-ANGLE,false,paint);
        
    }

}
// 布局文件


 3.2 :在仪表盘DashBoardView上画刻度

每一个小刻度实际上就是一个小矩形,我们可以通过PathDashEffect来画刻度

Path dash = new Path();//刻度

// 绘制一个小刻度矩形,每个分割点就是一个矩形,最后一个参数是顺时针
dash.addRect(0, 0, Utils.dp2px(2), Utils.dp2px(10), Path.Direction.CW);

       //计算总弧形长度 算出平均间隔
        PathMeasure pathMeasure = new PathMeasure(arc, false);//是否闭合

        //参数1 刻度本体。 参数2 间隔距离(弧形长度 - 刻度的厚度  然后平均分20分)。参数3 起始第一个距离开头。 参数4 旋转类型
        effect = new PathDashPathEffect(dash, (pathMeasure.getLength() - Utils.dp2px(2)) / 20, 0, PathDashPathEffect.Style.ROTATE);


DashBoardView.java

public DashBoardView extends View {

    ........
    
    //  1: 定义大弧的缺口角度和半径
    private static final int DASH_BOARD_ANGLT = 120 ; 
   
    // 2: 定义仪表板的半径
    private static final int DASH_BOARD_DADIUS = Utils.dp2px(150);
    public void onDraw(Canvas canvas) {
        
        super.onDraw(canvas);

        // 3: 画弧线
        // 3.1 先确定在页面的位置 -----比如在页面中心,可以通过下面的矩形确定位置
       //  Reacf reacf = new Reacf (left,top,right,bottom);
           Reacf arcReacf = new Reacf (getWidth()/2 - RADIUS , getHeight()/2-RADIUS,
              getWidth()/2 + RADIUS , getHeight()/2 + RADIUS);
       canvas.drawArc (reacf, 90+ ANGLE/2 , 360-ANGLE,false,paint);


      // 4: 画刻度
      
        paint.setPathEffect(effect);
    
        paint.setPathEffect(null);//置空

    }

}

 3.3 :在仪表盘DashBoardView上画指针

 画指针需要知道两个点: 起始点(startX , startY), 2: 终点(stopX, stopY)

终点可以根据刻度计算角度,然后通过 正弦或者余弦函数求解

终点要各自加上了仪表盘中心的位置,也就屏幕的宽高一半。否则线就画出屏幕了。因为终点是依据弧形原点画的。

startX :  getWidth()/2
startY :  getHeigth()/2

stopX :   (float) Math.cos(Math.toRadians(getAngleFromMark(5))) * LENGTH + getWidth() / 2

stopY :  (float) Math.sin(Math.toRadians(getAngleFromMark(5))) * LENGTH + getHeight() / 2

//  根据刻度求角度
    private int getAngleFromMark(int mark) {
        return (int) (90 + (float) ANGLE / 2 + (360 - (float) ANGLE) / 20 * mark);
    }


canvas.drawLine (startX, startY, stopX,stopY);

 

3.4 完整代码
public class DashBoard extends View {
    private static final int ANGLE = 120;//仪表盘 缺口 弧度
    private static final float LENGTH = Utils.dp2px(100);//指针长度
    private static final float RADIUS = Utils.dp2px(150);//半径
    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿标志
    Path dash = new Path();//刻度
    PathDashPathEffect effect;//刻度间隔

    public DashBoard(Context context) {
        super(context);
    }

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

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

    {//直接写大括号,会在所有构造方法super方法执行之后  执行
        paint.setStyle(Paint.Style.STROKE);//行程
        paint.setStrokeWidth(Utils.dp2px(2));//弧线宽度
        //----画弧线----
        Path arc = new Path();
        //参数,先确定这个弧线的矩形。 起始点角度。 扫过的角度
        arc.addArc(getWidth()/2 -RADIUS, getHeight()/2 - RADIUS, getWidth()/2 + RADIUS, getHeight()/2 +RADIUS,
                90 + ANGLE/2, 360 - ANGLE);
        //----画刻度----
        dash.addRect(0, 0, Utils.dp2px(2), Utils.dp2px(10), Path.Direction.CW);//每个分割点就是一个矩形,最后一个参数是顺时针
        //计算总弧形长度 算出平均间隔
        PathMeasure pathMeasure = new PathMeasure(arc, false);//是否闭合
        //参数1 刻度本体。 参数2 间隔距离(弧形长度 - 刻度的厚度  然后平均分20分)。参数3 起始第一个距离开头。 参数4 旋转类型
        effect = new PathDashPathEffect(dash, (pathMeasure.getLength() - Utils.dp2px(2)) / 20, 0, PathDashPathEffect.Style.ROTATE);

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);

        //画弧  useCenter 是否往圆心里画
        canvas.drawArc(getWidth()/2 -RADIUS, getHeight()/2 - RADIUS, getWidth()/2 + RADIUS, getHeight()/2 +RADIUS,
                90 + ANGLE/2, 360 - ANGLE, false, paint);

        //画刻度
        paint.setPathEffect(effect);
        canvas.drawArc(getWidth()/2 -RADIUS, getHeight()/2 - RADIUS, getWidth()/2 + RADIUS, getHeight()/2 +RADIUS,
                90 + ANGLE/2, 360 - ANGLE, false, paint);
        paint.setPathEffect(null);//置空

        //画指针
        canvas.drawLine(getWidth() / 2, getHeight() / 2,
                (float) Math.cos(Math.toRadians(getAngleFromMark(5))) * LENGTH + getWidth() / 2,
                        (float) Math.sin(Math.toRadians(getAngleFromMark(5))) * LENGTH + getHeight() / 2,
                paint);
    }

    //根据刻度求角度
    private int getAngleFromMark(int mark) {
        return (int) (90 + (float) ANGLE / 2 + (360 - (float) ANGLE) / 20 * mark);
    }





3.5 :绘制刻度上的数字

根据 drawText ( String textValue, float X, float Y, paint) 函数我们知道,绘制Text关键在于 x轴和Y轴坐标

3.5.1 :总的刻度数 -----得到等分刻度份数(20等分) 3.5.2 :有个等分份数结合总的弧长 ------得到每个等分的弧长,然后通过函数将弧长转化成角度 3.5.3 :有了角度通过sin或者cos函数----就可以计算出X轴和Y轴坐标 
 private void drawTextCircle(Canvas canvas) {
        float α;
        float[] p;
        float angle = mSweepAngle * 1f / mSection;
        for (int i = 0; i <= mSection; i++) {
            α = mStartAngle + angle * i;
            p = getCoordinatePoint(mRadius - mLength2, α);
            if (α % 360 > 135 && α % 360 < 225) {
                mTextPaint.setTextAlign(Paint.Align.LEFT);
            } else if ((α % 360 >= 0 && α % 360 < 45) || (α % 360 > 315 && α % 360 <= 360)) {
                mTextPaint.setTextAlign(Paint.Align.RIGHT);
            } else {
                mTextPaint.setTextAlign(Paint.Align.CENTER);
            }
            mTextPaint.getTextBounds(mHeaderText, 0, mTexts[i].length(), mRectText);
            int txtH = mRectText.height();
            if (i <= 1 || i >= mSection - 1) {
                canvas.drawText(mTexts[i], p[0], p[1] + txtH / 2, mTextPaint);
            } else if (i == 3) {
                canvas.drawText(mTexts[i], p[0] + txtH / 2, p[1] + txtH, mTextPaint);
            } else if (i == mSection - 3) {
                canvas.drawText(mTexts[i], p[0] - txtH / 2, p[1] + txtH, mTextPaint);
            } else {
                canvas.drawText(mTexts[i], p[0], p[1] + txtH, mTextPaint);
            }
        }
    }
/**
     * 依圆心坐标,半径,扇形角度,计算出扇形终射线与圆弧交叉点的xy坐标
     */
    public float[] getCoordinatePoint(int radius, float angle) {
        float[] point = new float[2];

        double arcAngle = Math.toRadians(angle); //将角度转换为弧度
        if (angle < 90) {
            point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
        } else if (angle == 90) {
            point[0] = mCenterX;
            point[1] = mCenterY + radius;
        } else if (angle > 90 && angle < 180) {
            arcAngle = Math.PI * (180 - angle) / 180.0;
            point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY + Math.sin(arcAngle) * radius);
        } else if (angle == 180) {
            point[0] = mCenterX - radius;
            point[1] = mCenterY;
        } else if (angle > 180 && angle < 270) {
            arcAngle = Math.PI * (angle - 180) / 180.0;
            point[0] = (float) (mCenterX - Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
        } else if (angle == 270) {
            point[0] = mCenterX;
            point[1] = mCenterY - radius;
        } else {
            arcAngle = Math.PI * (360 - angle) / 180.0;
            point[0] = (float) (mCenterX + Math.cos(arcAngle) * radius);
            point[1] = (float) (mCenterY - Math.sin(arcAngle) * radius);
        }

        return point;
    }

 

4:指针根据条件移动 5:总结

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/992714.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-21
下一篇 2022-05-21

发表评论

登录后才能评论

评论列表(0条)

保存