阅读 632

自定义View合辑(6)-波浪(贝塞尔曲线)

为了加强对自定义 View 的认知以及开发能力,我计划这段时间陆续来完成几个难度从易到难的自定义 View,并简单的写几篇博客来进行介绍,所有的代码也都会开源,也希望读者能给个 star 哈 GitHub 地址:github.com/leavesC/Cus… 也可以下载 Apk 来体验下:www.pgyer.com/CustomView

先看下效果图:

一、思路解析

波浪 View(即 WaveView)的重点在于其 onDraw 方法的十行代码上,当中运用到了贝塞尔曲线的知识

    //每个波浪的起伏高度
    private float waveHeight;

    //每个波浪的宽度
    private float waveWidth;

    //波浪的速度
    private long speed = DEFAULT_SPEED;

    private float animatedValue;

    private Path path = new Path();

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        path.reset();
        path.moveTo(-waveWidth + animatedValue, contentHeight / 2);
        for (float i = -waveWidth; i < contentWidth + waveWidth; i += waveWidth) {
            path.rQuadTo(waveWidth / 4, -waveHeight, waveWidth / 2, 0);
            path.rQuadTo(waveWidth / 4, waveHeight, waveWidth / 2, 0);
        }
        path.lineTo(contentWidth, contentHeight);
        path.lineTo(0, contentHeight);
        path.close();
        canvas.drawPath(path, paint);
    }
复制代码

从图片可以看出来各个波浪的起伏高度和宽度都是一样的,意味着在贝塞尔曲线中控制点的 Y 坐标是保持不变的,以上的逻辑可以利用下图来帮助理解

蓝色背景代表的是 View 所占的面积,红色小球连起来的曲线轨迹即为波浪的运行轨迹waveWidth 代表的是每个波浪的宽度,即每一个绿色方块的宽度。两个 path.rQuadTo 方法所绘制出来的分别是向上的曲线和向下的曲线,并在 for 循环中不断重复这个过程,直到绿色方块所占的总宽度超出 View 的宽度为止

为了呈现出**“波浪向右前进”的效果,当中就需要用到动画值 animatedValue 来不断改变贝塞尔曲线的起始坐标点**


    private ValueAnimator valueAnimator;

    public void initAnimation() {
        valueAnimator = new ValueAnimator();
        valueAnimator.setDuration(speed);
        valueAnimator.setRepeatCount(ValueAnimator.INFINITE);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                animatedValue = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
    }
复制代码

然后向外部开放改变波浪宽度、波浪高度、动画时长的这三个方法,即可以此来改变 View 的形状

    public void setWaveScaleWidth(float waveScaleWidth) {
        if (waveScaleWidth <= 0 || waveScaleWidth > 1) {
            return;
        }
        this.waveScaleWidth = waveScaleWidth;
        resetWaveParams();
    }

    public void setWaveScaleHeight(float waveScaleHeight) {
        if (waveScaleWidth <= 0 || waveScaleWidth > 1) {
            return;
        }
        this.waveScaleHeight = waveScaleHeight;
        resetWaveParams();
    }

    public void setSpeed(long speed) {
        this.speed = speed;
        resetWaveParams();
    }

    private void resetWaveParams() {
        waveWidth = contentWidth * waveScaleWidth;
        waveHeight = contentHeight * waveScaleHeight;
        if (valueAnimator != null) {
            valueAnimator.setFloatValues(0, waveWidth);
            valueAnimator.setDuration(speed);
        }
    }
复制代码
关注下面的标签,发现更多相似文章
评论