Android 自定义View 之 Path PathMeasure (二)

最近项目要求做一个进度条,头部有一个龟头小圆,如下:

微信图片_20200907184109.png

拿到图第一时间就是对整个图片进行技术拆分,由内到外可分为5个部分:


456.png

1.绘制实心圆

Paint pain=new Paint;
pain.setStyle(Paint.Style.FILL);
addCircle(float x, float y, float radius, @NonNull Direction dir)

2.绘制二阶贝塞尔曲线

quadTo(float x1, float y1, float x2, float y2)

3.根据4的轨迹通过PathMeasure 截取 片段

getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

4.绘制空心圆

Paint pain=new Paint;
pain.setStyle(Paint.Style.STROKE);
addCircle(float x, float y, float radius, @NonNull Direction dir)

5.一个外圈空心圆包裹一个内圈实心圆

pathMeasure.getPosTan

1和2部分下一篇博客加上,因为涉及贝塞尔曲线
好了, 来看看3-5核心代码。

    private void initView(Context context, AttributeSet attrs) {
        //内圈画笔
        insidePaint = new Paint();
        insidePaint.setAntiAlias(true);//去锯齿
        insidePaint.setStyle(Paint.Style.STROKE);
        insidePaint.setStrokeWidth(insideStrokeWidth);
        insidePaint.setColor(Color.WHITE);


        //外圈画笔
        outsidePaint = new Paint();
        outsidePaint.setAntiAlias(true);//去锯齿
        outsidePaint.setStyle(Paint.Style.STROKE);
        outsidePaint.setStrokeWidth(outSideStrokeWidth);
        outsidePaint.setColor(Color.WHITE);


        //小龟头空心圆圈画笔
        smallPaint = new Paint();
        smallPaint.setAntiAlias(true);
        smallPaint.setStyle(Paint.Style.STROKE);
        smallPaint.setStrokeWidth(smallStrokeWidth);
        smallPaint.setColor(Color.WHITE);

        //小龟头内圈实体圆圈画笔
        smallInsidePaint = new Paint();
        smallInsidePaint.setAntiAlias(false);
        smallInsidePaint.setStyle(Paint.Style.FILL);
        smallInsidePaint.setColor(getResources().getColor(R.color.color_5FBBEB));
    }

大致是初始化了4个画笔,从上到下分别负责:4-3-5


    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        outSidePath = new Path();
        insidePath = new Path();

        insidePath.addCircle((w) / 2f + insideStrokeWidth
                , (h) / 2f + insideStrokeWidth
                , (h) / 2f - 2 * insideStrokeWidth - outSideStrokeWidth
                , Path.Direction.CW);


     
        pathMeasure.setPath(insidePath, true);
    }

在 sizeChanged阶段 初始化 外圈 内圈 的路径Path,并且初始化内圈半径参数

核心部分

   @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        outSidePath.reset();
        float length = pathMeasure.getLength();
        canvas.drawPath(insidePath, insidePaint);
        float startD = 0;
        float stopD = (Math.abs(progress)) / 100f * length;
        pathMeasure.getSegment(startD, stopD, outSidePath, true);

        if (progress != 1) {
            canvas.drawPath(outSidePath, outsidePaint);
        }

        pathMeasure.getPosTan(stopD + 2 + outSideStrokeWidth, pos, tan);

        Log.d(TAG, "THE POS IS:" + pos[0] + "tan:" + tan[0] + "...stopD:" + stopD);
        if (progress != 100 && progress != 1) {
            //绘制龟头外圈
            canvas.drawCircle(pos[0], pos[1], outSideRadius, smallPaint);
            //绘制龟头外圈
            canvas.drawCircle(pos[0], pos[1], (outSideRadius - smallStrokeWidth / 2), smallInsidePaint);
        }
    }

绘制阶段大部分都是计算:有截取片段的开始startD 、stopD和 求曲线路径当前正切坐标值getPosTan
通过外部传进来的Progress 进度控制 截取长度,进而达到类似progress 一直在动的效果。
最后得到初步效果如下:

ps:下方黑色的动效为:[Android 自定义View 之 Path PathMeasure (一)]

VID_20200907183216.gif

刚开始写博客,格式不是很好,凑合着看吧,有需要的可以私信 ~

你可能感兴趣的:(Android 自定义View 之 Path PathMeasure (二))