自定義View合輯(6)-波浪(貝塞爾曲線)

leavesC發表於2019-05-08

為了加強對自定義 View 的認知以及開發能力,我計劃這段時間陸續來完成幾個難度從易到難的自定義 View,並簡單的寫幾篇部落格來進行介紹,所有的程式碼也都會開源,也希望讀者能給個 star 哈 GitHub 地址:github.com/leavesC/Cus… 也可以下載 Apk 來體驗下:www.pgyer.com/CustomView

先看下效果圖:

自定義View合輯(6)-波浪(貝塞爾曲線)

一、思路解析

波浪 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合輯(6)-波浪(貝塞爾曲線)

藍色背景代表的是 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);
        }
    }
複製程式碼

相關文章