自定义View进阶之贝济埃(或贝塞尔)曲线水波纹绘制

Posted LQS_Android

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了自定义View进阶之贝济埃(或贝塞尔)曲线水波纹绘制相关的知识,希望对你有一定的参考价值。

     在Path的系列函数中,除了一些基本的设置和绘图用法外,还有一个强大的工具一一贝济埃曲线 它能将利用 moveTo()、LineTo() 连接的生硬路径变得平滑,也能够实现很多炫酷的效 ,比如水波纹等。

贝济埃曲线的历史:
贝济埃曲线于 1962 年,由法国工程师皮埃尔·贝济埃(Pierre Bézier)所广泛发表,他运用贝济埃曲线来为汽车的主体进行设计,贝济埃曲线最初由保尔·德·卡斯特里奥于1959年运用德.卡斯特里奥算法开发,以稳定数值的方法求出贝济埃曲线(又称为贝塞尔曲线)。

贝塞尔曲线有着很多特殊的性质, 在图形设计和路径规划中应用都非常广泛,在路径规划中贝塞尔曲线完全由其控制点决定其形状,n个控制点对应着n-1阶的贝塞尔曲线,并且可以通过递归的方式来绘制。

一阶贝济埃曲线

一阶贝济埃曲线的公式如下:
曲线演示效果:
在这里,P0为起始点,P1 为终点, t 表示当前时间, B(t) 表示公式的结果值。注意, 曲线 的含义就是随着时间的变化,公式的结果值B(t)所形成的轨迹 。在动画 中,黑色点表示在当前时间t下公式B(t)的取值;而红色的那条线就表示在各个时间点下不同取值的 B(t)所形成的轨迹。
 
对于一 阶贝济埃曲线,可以理解为在由起始点和终点形成的这条直线上匀速移动的点。

 

二阶贝济埃曲线

二阶贝济埃曲线的公式如下:
 
在这里, P0 是起始点 ,P2是 终点, P1 是控制点。
假设将时间定在 t= 0.3和t=0.8的时候   ,状 态如下图所示:
完整二阶贝济埃曲线示意图:
    首先, P0、 P1 形成了一 条一阶贝济埃曲线,我们讲过,一阶贝济埃曲线就是一 个点在这
条直线上做匀速运动,所以在 P0 P1 这条直线上匀速运动的点就是 Q0。
   
   其次 P1、P2 形成了一条一阶贝济埃曲线,在这条一阶贝济埃曲线上 随时间移动的点是 Q1。
   
   最后,动态点 Q0、 Q1 又形成了一条一阶济埃曲线,在这条一 阶贝济 埃曲线上动态移
动的点是B ,而B 点的移动轨迹就是二阶贝济埃曲线的最终形态。
 
从上面的讲解中大家也可以知道,之所以称为二 阶贝济埃曲线,是因为B 点的移动轨迹
是建立在两条一阶贝济埃曲线的中间点Q0、Q1   的基础上的。
 

三阶贝济埃曲线

三阶贝济埃曲线的公式如下:

 
同样 ,P0 是起始点, P3 是终点,P1 是第 个控制点,P2第二 个控制点。
首先,这里有三条一 阶贝济埃曲线,分别是 P0 - P1、P1 -P2、P2-P3  ,它 们随时间变化的点 分别为Q0、Q1、 Q2。
 
然后由Q0、Q1、 Q2这三个点再次连接,形成两条一 阶贝济埃曲线,分别是 Q0-Q1,Q1- Q2,随时间变化的点为R0、R1。同样,R0和R1可以连接形成一条一阶贝济埃曲线,在 R0 -R1 这条贝 济埃曲线上随时间移动的点是B ,而B 点的移动轨迹就是三 阶贝济埃曲线的最终形状。

 

同理,还有四阶贝济埃曲线、五阶贝济埃曲线,在android开发中最多用到二阶、三阶贝济埃曲线,所以四、五阶的贝济埃曲线不再总结。

贝济埃曲线开发常用API

 
二阶贝济埃曲线
 
① quadTo()函数
 /**
     * Add a quadratic bezier from the last point, approaching control point
     * (x1,y1), and ending at (x2,y2). If no moveTo() call has been made for
     * this contour, the first point is automatically set to (0,0).
     *
     * @param x1 The x-coordinate of the control point on a quadratic curve
     * @param y1 The y-coordinate of the control point on a quadratic curve
     * @param x2 The x-coordinate of the end point on a quadratic curve
     * @param y2 The y-coordinate of the end point on a quadratic curve
     */
    public void quadTo(float x1, float y1, float x2, float y2) {
        isSimplePath = false;
        nQuadTo(mNativePath, x1, y1, x2, y2);
    }

参数中,(x1,y1)是控制点坐标,(x2,y2)是终点坐标.

大家可能会有一个疑 问: 只 有控制点和终点坐标,那起始点坐标是多少呢?
整条线的起始点是通过 Pa th.mo veTo(x,y )函数来指定的,而如果我们连续调用 qu ad To ()函数,那么前一个 quad To ()函 数的终点就是下一个 quad To ()函数的起始点;如果初始没有调用 Path.moveTo(x,y) 函数来指定起始点,则默认以控件左上角点( 0,0 )为起始点
 
② rQuadTo()函数
 /**
     * Same as quadTo, but the coordinates are considered relative to the last
     * point on this contour. If there is no previous point, then a moveTo(0,0)
     * is inserted automatically.
     *
     * @param dx1 The amount to add to the x-coordinate of the last point on
     *            this contour, for the control point of a quadratic curve
     * @param dy1 The amount to add to the y-coordinate of the last point on
     *            this contour, for the control point of a quadratic curve
     * @param dx2 The amount to add to the x-coordinate of the last point on
     *            this contour, for the end point of a quadratic curve
     * @param dy2 The amount to add to the y-coordinate of the last point on
     *            this contour, for the end point of a quadratic curve
     */
    public void rQuadTo(float dx1, float dy1, float dx2, float dy2) {
        isSimplePath = false;
        nRQuadTo(mNativePath, dx1, dy1, dx2, dy2);
    }

quadTo()函数和rQuadTo()函数使用是等价转换,意义都是一样的:
 
 

 知道了上面,两个二阶贝塞尔曲线的API之后,我们在实际的应用中使用一下:

实例一:利用贝塞尔曲线自定义View跟踪手势轨迹绘制平滑曲线,代码如下:

package com.xw.beziercustom.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/6/11
 * @description: 贝塞尔曲线(Bézier curve),又称贝兹曲线或贝济埃曲线
 */
public class BezierGestureTrackView extends View {
    private Path mPath = new Path();
    private Paint mPaint;
    private float mPreX,mPreY;

    public BezierGestureTrackView(Context context, AttributeSet attrs) {
        super(context, attrs);


        mPaint = new Paint();
        mPaint.setColor(Color.BLACK);
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(5);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()){
            case MotionEvent.ACTION_DOWN:{
                /**
                 * 贝济埃曲线是由起始点、控制点、终点确定平滑过渡、和绘制曲线的。
                 * 整条线的起始点是通过Path.moveTo(x,y)函数来指定的.
                 */
                mPath.moveTo(event.getX(),event.getY());
                /**
                 * 定义两个变量 mPreX,mPreY来表示手指的前一个点,这个点是用来做控制点的。
                 */
                mPreX = event.getX();
                mPreY = event.getY();
                return true;
            }
            case MotionEvent.ACTION_MOVE:{
                /**
                 * 计算贝济埃曲线的终点
                 */
                float endX = (mPreX+event.getX())/2;
                float endY = (mPreY+event.getY())/2;
                /**
                 * public void quadTo(float x1 , float y1 , float x2 , float y2)
                 * 参数中(x1 ,y1) 是控制点坐标,(x2,y2)是终点坐标
                 */
                mPath.quadTo(mPreX,mPreY,endX,endY);
                mPreX = event.getX();
                mPreY =event.getY();
                invalidate();
            }
            break;
            default:
                break;
        }
        return super.onTouchEvent(event);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.GREEN);
        canvas.drawPath(mPath,mPaint);
    }
}
运行效果图,我们用手指随便写两个大写字母:
 
实例二:自定义View利用贝塞尔曲线实现水波纹效果,代码如下:
 
package com.xw.beziercustom.view;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.animation.LinearInterpolator;

/**
 * Copyright (c)2021 网络科技有限公司
 *
 * @author: LQS
 * @date: 2021/6/14
 * @description: com.xw.beziercustom.view.AnimWaveView
 */
public class AnimWaveView extends View {
    private Paint mPaint;
    private Path mPath;
    private int mItemWaveLength = 1200;
    private int dx;

    public AnimWaveView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mPath = new Path();
        mPaint = new Paint();
        mPaint.setColor(Color.GREEN);
        mPaint.setStyle(Paint.Style.FILL);

        /**
         * 让波纹动起来其实很简单,在调用 path.moveTo()函数的时候,将起始点向右移动即可实现波纹移动
         * 而且只要移动一个波长的长度波纹就会重合,就可以实现无限循环移动。
         */
        //ValueAnimator对指定值区间(0, mItemWaveLength)做动画运算,我们通过对运算过程做监听来自己操作控件。
        ValueAnimator animator = ValueAnimator.ofInt(0, mItemWaveLength);
        animator.setDuration(3000);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setInterpolator(new LinearInterpolator());
        //监听ValueAnimator的动画过程来自己对控件做操作
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            public void onAnimationUpdate(ValueAnimator animation) {
                //通过animation.getAnimatedValue()得到动画运算的当前值.
                dx = (Integer) animation.getAnimatedValue();
                Log.d("getAnimatedValue","animation.getAnimatedValue():"+dx);
                postInvalidate();
            }
        });
        animator.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);
        mPath.reset();
        int originY = 300;
        int halfWaveLen = mItemWaveLength / 2;
        /**
         * 将mPath的起始位置向左移动一个波长的的距离,为后续实现波形移动动画做准备。
         * 动画的长度为一个波长,将当前值保存在类的成员变量dx中
         */
        mPath.moveTo(-mItemWaveLength + dx, originY);
        for (int i = -mItemWaveLength; i <= getWidth() + mItemWaveLength; i += mItemWaveLength) {
            //画的是一个波长中的前半个波(包含波峰的部分,即dy1为-150,Y轴方向上绘制的点更靠近屏幕顶部)
            mPath.rQuadTo(halfWaveLen / 2, -150, halfWaveLen, 0);
            //画的是一个波长中的后半个波(包含波谷的部分,即dy1为+150,Y轴方向上绘制的点更远离屏幕顶部)
            mPath.rQuadTo(halfWaveLen / 2, 150, halfWaveLen, 0);
        }

        /**
         * 下图中黑线所标出的区域就是利利lineTo()函数闭合的区域, 屏幕两边多出的波形在闭后是看不到的
         * lineTo(float x, float y):从最后一点到指定点(x,y)添加一条线。如果没有对此轮廓进行moveTo()调用,则第一个点将自动设置为(0,0)。
         */
        mPath.lineTo(getWidth(), getHeight());
        mPath.lineTo(0, getHeight());
        mPath.close();

        canvas.drawPath(mPath, mPaint);
    }

}

 在布局文件中使用上面自定义的View组件:

<?xml version="1.0" encoding="utf-8"?>
<com.xw.beziercustom.view.AnimWaveView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="300dp"
    tools:context=".MainActivity">

</com.xw.beziercustom.view.AnimWaveView>

其中关于通过animation.getAnimatedValue()得到动画运算的当前值,是ValueAnimator对指定值区间(0, mItemWaveLength)做动画运算得到的,它的log输出如下:

D/getAnimatedValue: animation.getAnimatedValue():3
D/getAnimatedValue: animation.getAnimatedValue():138
D/getAnimatedValue: animation.getAnimatedValue():10
D/getAnimatedValue: animation.getAnimatedValue():145
D/getAnimatedValue: animation.getAnimatedValue():17
D/getAnimatedValue: animation.getAnimatedValue():151
D/getAnimatedValue: animation.getAnimatedValue():24
D/getAnimatedValue: animation.getAnimatedValue():158
D/getAnimatedValue: animation.getAnimatedValue():30
D/getAnimatedValue: animation.getAnimatedValue():164
D/getAnimatedValue: animation.getAnimatedValue():37
D/getAnimatedValue: animation.getAnimatedValue():171
D/getAnimatedValue: animation.getAnimatedValue():44
D/getAnimatedValue: animation.getAnimatedValue():178
D/getAnimatedValue: animation.getAnimatedValue():50
D/getAnimatedValue: animation.getAnimatedValue():184
D/getAnimatedValue: animation.getAnimatedValue():57
D/getAnimatedValue: animation.getAnimatedValue():191
D/getAnimatedValue: animation.getAnimatedValue():63
D/getAnimatedValue: animation.getAnimatedValue():198
D/getAnimatedValue: animation.getAnimatedValue():70
D/getAnimatedValue: animation.getAnimatedValue():204
D/getAnimatedValue: animation.getAnimatedValue():77
D/getAnimatedValue: animation.getAnimatedValue():211
D/getAnimatedValue: animation.getAnimatedValue():84
D/getAnimatedValue: animation.getAnimatedValue():218
D/getAnimatedValue: animation.getAnimatedValue():90
D/getAnimatedValue: animation.getAnimatedValue():224
D/getAnimatedValue: animation.getAnimatedValue():97
D/getAnimatedValue: animation.getAnimatedValue():231
D/getAnimatedValue: animation.getAnimatedValue():104
D/getAnimatedValue: animation.getAnimatedValue():238
D/getAnimatedValue: animation.getAnimatedValue():110
D/getAnimatedValue: animation.getAnimatedValue():244
D/getAnimatedValue: animation.getAnimatedValue():117
D/getAnimatedValue: animation.getAnimatedValue():251
D/getAnimatedValue: animation.getAnimatedValue():123
D/getAnimatedValue: animation.getAnimatedValue():257
D/getAnimatedValue: animation.getAnimatedValue():130
D/getAnimatedValue: animation.getAnimatedValue():264
D/getAnimatedValue: animation.getAnimatedValue():137
D/getAnimatedValue: animation.getAnimatedValue():271
D/getAnimatedValue: animation.getAnimatedValue():143
D/getAnimatedValue: animation.getAnimatedValue():278
D/getAnimatedValue: animation.getAnimatedValue():150
D/getAnimatedValue: animation.getAnimatedValue():284
D/getAnimatedValue: animation.getAnimatedValue():157
D/getAnimatedValue: animation.getAnimatedValue():291
D/getAnimatedValue: animation.getAnimatedValue():163
D/getAnimatedValue: animation.getAnimatedValue():298
D/getAnimatedValue: animation.getAnimatedValue():170
D/getAnimatedValue: animation.getAnimatedValue():304
D/getAnimatedValue: animation.getAnimatedValue():177
D/getAnimatedValue: animation.getAnimatedValue():311
D/getAnimatedValue: animation.getAnimatedValue():183
D/getAnimatedValue: animation.getAnimatedValue():317
D/getAnimatedValue: animation.getAnimatedValue():190
D/getAnimatedValue: animation.getAnimatedValue():324
D/getAnimatedValue: animation.getAnimatedValue():197
D/getAnimatedValue: animation.getAnimatedValue():331
D/getAnimatedValue: animation.getAnimatedValue():203
D/getAnimatedValue: animation.getAnimatedValue():337
D/getAnimatedValue: animation.getAnimatedValue():210
D/getAnimatedValue: animation.getAnimatedValue():344
D/getAnimatedValue: animation.getAnimatedValue():216
D/getAnimatedValue: animation.getAnimatedValue():351
D/getAnimatedValue: animation.getAnimatedValue():223
D/getAnimatedValue: animation.getAnimatedValue():358
D/getAnimatedValue: animation.getAnimatedValue():230
D/getAnimatedValue: animation.getAnimatedValue():364
D/getAnimatedValue: animation.getAnimatedValue():236
D/getAnimatedValue: animation.getAnimatedValue():371
D/getAnimatedValue: animation.getAnimatedValue():243
D/getAnimatedValue: animation.getAnimatedValue():378
D/getAnimatedValue: animation.getAnimatedValue():250
D/getAnimatedValue: animation.getAnimatedValue():384
D/getAnimatedValue: animation.getAnimatedValue():256
D/getAnimatedValue: animation.getAnimatedValue():391
D/getAnimatedValue: animation.getAnimatedValue():263
D/getAnimatedValue: animation.getAnimatedValue():397
D/getAnimatedValue: animation.getAnimatedValue():270
D/getAnimatedValue: animation.getAnimatedValue():404
D/getAnimatedValue: animation.getAnimatedValue():276
D/getAnimatedValue: animation.getAnimatedValue():411
D/getAnimatedValue: animation.getAnimatedValue():283
D/getAnimatedValue: animation.getAnimatedValue():417
D/getAnimatedValue: animation.getAnimatedValue():290
D/getAnimatedValue: animation.getAnimatedValue():424
D/getAnimatedValue: animation.getAnimatedValue():296
D/getAnimatedValue: animation.getAnimatedValue():431
D/getAnimatedValue: animation.getAnimatedValue():303
D/getAnimatedValue: animation.getAnimatedValue():438
D/getAnimatedValue: animation.getAnimatedValue():310
D/getAnimatedValue: animation.getAnimatedValue():444
D/getAnimatedValue: animation.getAnimatedValue():316
D/getAnimatedValue: animation.getAnimatedValue():451
D/getAnimatedValue: animation.getAnimatedValue():323
D/getAnimatedValue: animation.getAnimatedValue():458
D/getAnimatedValue: animation.getAnimatedValue():329
D/getAnimatedValue: animation.getAnimatedValue():464
D/getAnimatedValue: animation.getAnimatedValue():336
D/getAnimatedValue: animation.getAnimatedValue():471
D/getAnimatedValue: animation.getAnimatedValue():343
D/getAnimatedValue: animation.getAnimatedValue():477
D/getAnimatedValue: animation.getAnimatedValue():350
D/getAnimatedValue: animation.getAnimatedValue():484
D/getAnimatedValue: animation.getAnimatedValue():356
D/getAnimatedValue: animation.getAnimatedValue():491
D/getAnimatedValue: animation.getAnimatedValue():363
D/getAnimatedValue: animation.getAnimatedValue():497
D/getAnimatedValue: animation.getAnimatedValue():370
D/getAnimatedValue: animation.getAnimatedValue():504
D/getAnimatedValue: animation.getAnimatedValue():376
D/getAnimatedValue: animation.getAnimatedValue():511
D/getAnimatedValue: animation.getAnimatedValue():383
D/getAnimatedValue: animation.getAnimatedValue():518
D/getAnimatedValue: animation.getAnimatedValue():389
D/getAnimatedValue: animation.getAnimatedValue():524
D/getAnimatedValue: animation.getAnimatedValue():396
D/getAnimatedValue: animation.getAnimatedValue():531
D/getAnimatedValue: animation.getAnimatedValue():403
D/getAnimatedValue: animation.getAnimatedValue():538
D/getAnimatedValue: animation.getAnimatedValue():409
D/getAnimatedValue: animation.getAnimatedValue():544
D/getAnimatedValue: animation.getAnimatedValue():416
D/getAnimatedValue: animation.getAnimatedValue():551
D/getAnimatedValue: animation.getAnimatedValue():423
D/getAnimatedValue: animation.getAnimatedValue():557
D/getAnimatedValue: animation.getAnimatedValue():430
D/getAnimatedValue: animation.getAnimatedValue():564
D/getAnimatedValue: animation.getAnimatedValue():436
D/getAnimatedValue: animation.getAnimatedValue():571
D/getAnimatedValue: animation.getAnimatedValue():443
D/getAnimatedValue: animation.getAnimatedValue():577
D/getAnimatedValue: animation.getAnimatedValue():450
D/getAnimatedValue: animation.getAnimatedValue():584
D/getAnimatedValue: animation.getAnimatedValue():456
D/getAnimatedValue: animation.getAnimatedValue():591
D/getAnimatedValue: animation.getAnimatedValue():463
D/getAnimatedValue: animation.getAnimatedValue():597
D/getAnimatedValue: animation.getAnimatedValue():469
D/getAnimatedValue: animation.getAnimatedValue():604
D/getAnimatedValue: animation.getAnimatedValue():476
D/getAnimatedValue: animation.getAnimatedValue():611
D/getAnimatedValue: animation.getAnimatedValue():483
D/getAnimatedValue: animation.getAnimatedValue():617
D/getAnimatedValue: animation.getAnimatedValue():490
D/getAnimatedValue: animation.getAnimatedValue():624
D/getAnimatedValue: animation.getAnimatedValue():496
D/getAnimatedValue: animation.getAnimatedValue():631
D/getAnimatedValue: animation.getAnimatedValue():503
D/getAnimatedValue: animation.getAnimatedValue():637
D/getAnimatedValue: animation.getAnimatedValue():510
D/getAnimatedValue: animation.getAnimatedValue():644
D/getAnimatedValue: animation.getAnimatedValue():516
D/getAnimatedValue: animation.getAnimatedValue():651
D/getAnimatedValue: animation.getAnimatedValue():523
D/getAnimatedValue: animation.getAnimatedValue():657
D/getAnimatedValue: animation.getAnimatedValue():529
D/getAnimatedValue: animation.getAnimatedValue():664
D/getAnimatedValue: animation.getAnimatedValue():536
D/getAnimatedValue: animation.getAnimatedValue():670
D/getAnimatedValue: animation.getAnimatedValue():543
D/getAnimatedValue: animation.getAnimatedValue():677
D/getAnimatedValue: animation.getAnimatedValue():549
D/getAnimatedValue: animation.getAnimatedValue():684
D/getAnimatedValue: animation.getAnimatedValue():556
D/getAnimatedValue: animation.getAnimatedValue():690
D/getAnimatedValue: animation.getAnimatedValue():563
D/getAnimatedValue: animation.getAnimatedValue():697
D/getAnimatedValue: animation.getAnimatedValue():570
D/getAnimatedValue: animation.getAnimatedValue():704
D/getAnimatedValue: animation.getAnimatedValue():576
D/getAnimatedValue: animation.getAnimatedValue():710
D/getAnimatedValue: animation.getAnimatedValue():583
D/getAnimatedValue: animation.getAnimatedValue():717
D/getAnimatedValue: animation.getAnimatedValue():590
D/getAnimatedValue: animation.getAnimatedValue():724
D/getAnimatedValue: animation.getAnimatedValue():596
D/getAnimatedValue: animation.getAnimatedValue():730
D/getAnimatedValue: animation.getAnimatedValue():603
D/getAnimatedValue: animation.getAnimatedValue():737
D/getAnimatedValue: animation.getAnimatedValue():609
D/getAnimatedValue: animation.getAnimatedValue():744
D/getAnimatedValue: animation.getAnimatedValue():616
D/getAnimatedValue: animation.getAnimatedValue():750
D/getAnimatedValue: animation.getAnimatedValue():623
D/getAnimatedValue: animation.getAnimatedValue():757
D/getAnimatedValue: animation.getAnimatedValue():629
D/getAnimatedValue: animation.getAnimatedValue():764
D/getAnimatedValue: animation.getAnimatedValue():636
D/getAnimatedValue: animation.getAnimatedValue():770
D/getAnimatedValue: animation.getAnimatedValue():643
D/getAnimatedValue: animation.getAnimatedValue():777
D/getAnimatedValue: animation.getAnimatedValue():649
D/getAnimatedValue: animation.getAnimatedValue():784
D/getAnimatedValue: animation.getAnimatedValue():656
D/getAnimatedValue: animation.getAnimatedValue():790
D/getAnimatedValue: animation.getAnimatedValue():663
D/getAnimatedValue: animation.getAnimatedValue():797
D/getAnimatedValue: animation.getAnimatedValue():669
D/getAnimatedValue: animation.getAnimatedValue():803
D/getAnimatedValue: animation.getAnimatedValue():676
D/getAnimatedValue: animation.getAnimatedValue():810
D/getAnimatedValue: animation.getAnimatedValue():683
D/getAnimatedValue: animation.getAnimatedValue():817
D/getAnimatedValue: animation.getAnimatedValue():689
D/getAnimatedValue: animation.getAnimatedValue():823
D/getAnimatedValue: animation.getAnimatedValue():696
D/getAnimatedValue: animation.getAnimatedValue():830
D/getAnimatedValue: animation.getAnimatedValue():703
D/getAnimatedValue: animation.getAnimatedValue():837
D/getAnimatedValue: animation.getAnimatedValue():709
D/getAnimatedValue: animation.getAnimatedValue():844
D/getAnimatedValue: animation.getAnimatedValue():716
D/getAnimatedValue: animation.getAnimatedValue():850
D/getAnimatedValue: animation.getAnimatedValue():722
D/getAnimatedValue: animation.getAnimatedValue():857
D/getAnimatedValue: animation.getAnimatedValue():729
D/getAnimatedValue: animation.getAnimatedValue():864
D/getAnimatedValue: animation.getAnimatedValue():736
D/getAnimatedValue: animation.getAnimatedValue():870
D/getAnimatedValue: animation.getAnimatedValue():742
D/getAnimatedValue: animation.getAnimatedValue():877
D/getAnimatedValue: animation.getAnimatedValue():749
D/getAnimatedValue: animation.getAnimatedValue():883
D/getAnimatedValue: animation.getAnimatedValue():756
D/getAnimatedValue: animation.getAnimatedValue():890
D/getAnimatedValue: animation.getAnimatedValue():762
D/getAnimatedValue: animation.getAnimatedValue():897
D/getAnimatedValue: animation.getAnimatedValue():769
D/getAnimatedValue: animation.getAnimatedValue():903
D/getAnimatedValue: animation.getAnimatedValue():776
D/getAnimatedValue: animation.getAnimatedValue():910
D/getAnimatedValue: animation.getAnimatedValue():782
D/getAnimatedValue: animation.getAnimatedValue():917
D/getAnimatedValue: animation.getAnimatedValue():789
D/getAnimatedValue: animation.getAnimatedValue():924
D/getAnimatedValue: animation.getAnimatedValue():796
D/getAnimatedValue: animation.getAnimatedValue():930
D/getAnimatedValue: animation.getAnimatedValue():802
D/getAnimatedValue: animation.getAnimatedValue():937
D/getAnimatedValue: animation.getAnimatedValue():809
D/getAnimatedValue: animation.getAnimatedValue():944
D/getAnimatedValue: animation.getAnimatedValue():816
D/getAnimatedValue: animation.getAnimatedValue():950
D/getAnimatedValue: animation.getAnimatedValue():822
D/getAnimatedValue: animation.getAnimatedValue():957
D/getAnimatedValue: animation.getAnimatedValue():829
D/getAnimatedValue: animation.getAnimatedValue():963
D/getAnimatedValue: animation.getAnimatedValue():836
D/getAnimatedValue: animation.getAnimatedValue():970
D/getAnimatedValue: animation.getAnimatedValue():842
D/getAnimatedValue: animation.getAnimatedValue():977
......
D/getAnimatedValue: animation.getAnimatedValue():1177

运行的动画截图如下:

完结!
欢迎留言、讨论!想要源码的可以评论区!
 
我所走过的岁月如水般流淌,皓月无垠。生命经历的坎坷,只有文字能懂的,铭记那逝去的分与毫!---LQS
 
 
 
 
 

以上是关于自定义View进阶之贝济埃(或贝塞尔)曲线水波纹绘制的主要内容,如果未能解决你的问题,请参考以下文章

自定义控件三部曲之绘图篇——Path之贝赛尔曲线和手势轨迹水波纹效果

Android自定义View——实现水波纹效果类似剩余流量球

Android进阶之自定义View实战贝塞尔曲线应用

Android进阶之自定义View实战贝塞尔曲线应用

贝塞尔曲线公式 求详细解释

贝塞尔曲线