自定义View,贝赛尔曲线实现水波纹进度条

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/zqd1984309864/article/details/81149405

最终的效果:

 思路就是在onDraw()中画一些内容,主要方法有这些:

/**
 * 剪裁圆形区域
 */
clipCircle(canvas);
/**
 * 画圆边线
 */
drawCircle(canvas);
/**
 * 画波浪线
 */
drawWave(canvas);
/**
 * 画进度文字
 */
drawText(canvas);

1.clipCircle(canvas)这个方法剪裁出一个圆形画布:

private void clipCircle(Canvas canvas) {
    Path circlePath = new Path();
    circlePath.addCircle(screenWidth / 2, screenHeignt / 2, screenHeignt / 2, Path.Direction.CW);
    canvas.clipPath(circlePath);
}

2.drawCircle(canvas)画出圆的边线:

private void drawCircle(Canvas canvas) {
        canvas.drawCircle(screenHeignt / 2, screenHeignt / 2, screenHeignt / 2, circlePaint);
    }

3.drawWave(canvas)画波浪线:

 private void drawWave(Canvas canvas) {
        int height = (int) (progress / 100 * screenHeignt);
        startPoint.y = -height;
        canvas.translate(0, screenHeignt);
        path = new Path();
        wavePaint.setStyle(Paint.Style.FILL);
        wavePaint.setColor(Color.GREEN);
        int wave = screenWidth / 4;
        path.moveTo(startPoint.x, startPoint.y);
        for (int i = 0; i < 4; i++) {
            int startX = startPoint.x + i * wave * 2;
            int endX = startX + 2 * wave;
            if (i % 2 == 0) {
                path.quadTo((startX + endX) / 2, startPoint.y + amplitude, endX, startPoint.y);
            } else {
                path.quadTo((startX + endX) / 2, startPoint.y - amplitude, endX, startPoint.y);
            }
        }
        path.lineTo(screenWidth, screenHeignt / 2);
        path.lineTo(-screenWidth, screenHeignt / 2);
        path.lineTo(-screenWidth, 0);
        path.close();
        canvas.drawPath(path, wavePaint);
        startPoint.x += 10;
        if (startPoint.x > 0) {
            startPoint.x = -screenWidth;
        }
        path.reset();
    }

这段代码说来有些复杂;

首先我们看到height,这个是波浪曲线所在的y轴的曲线,progress是传进来的进度为了计算我转化成百分比先除以100再乘以屏幕高度(这里的屏幕高度大家可以理解成直径)。

其次我把屏幕y轴移动到了控件的底部也就是如图这样的:

接下来我就开始创建path设置画笔开始画波浪线了, 我们设置的startpoint为x轴负一个屏幕的宽度,从这个点开始画两个完整的正弦曲线,wave的大小为screenWidth/4(在图中标注了),在for中改变startpoint并且画出曲线(每个人可能有不一样的实现方法,这段代码看起来可能费劲),最终把这个波浪与右下做下点连起来形成闭合图形,用于着色,那么画完了怎么让波浪冻起来呢?我们每次刷新ondraw()都让起始点的x+10,然后判断如果x大于0,也就是起始点来到了(0,screenWidth)这个点的时候,我们把startpoint的x坐标回到最初的位置即可,这样每次刷新就形成了一个动态波浪的效果了。波浪的高度通过刚开始的startPoint.y = -height来实现,加符号因为y轴复方向为进度的增长方向。

4.drawText()绘制文字:

private void drawText(Canvas canvas) {
        Rect targetRect = new Rect(0, -screenHeignt, screenWidth, 0);
        Paint.FontMetricsInt fontMetrics = textPaint.getFontMetricsInt();
        int baseline = (targetRect.bottom + targetRect.top - fontMetrics.bottom - fontMetrics.top) / 2;
        textPaint.setTextAlign(Paint.Align.CENTER);
        canvas.drawText(String.valueOf((int) textProgress + "%"), targetRect.centerX(), baseline, textPaint);
    }

 绘制文字为了让文字居中我做了一些必要的计算

PS:细心的小伙伴在setProgress()这个方法中发现了一点问题为什么会这么写呢:

public void setProgress(float progress) {
        this.textProgress = progress;
        if (progress == 100) {
            this.progress = progress + amplitude;
        } else {
            this.progress = progress;
        }
    }

原因是因为当我们的进度达到100%的时候,由于sin曲线和从左到右的波动还是会出现空白流动的区域,就像如图这样:

这样岂不是很蛋疼,进度满了不能出现这种情况,所以我手动判断如果进度在100%就把y坐标加上一个保险值,保证不出现以上这个情况。 

书写本文旨在练习与理解自定义view、贝赛尔曲线等等知识,如有错误欢迎指正,源码链接GitHub传送门,欢迎clone&start

猜你喜欢

转载自blog.csdn.net/zqd1984309864/article/details/81149405
今日推荐