Android自定義控件(二)——支付寶支付成功動畫

讀律看書三九年,烏紗頭上有青天,男兒欲畫凌煙閣,第一功名不愛錢。

不知道大家關注過沒有,在你使用支付寶的過程中,有一個支付成功的動畫,雖然說很小,但看起來其實還是蠻實用的,涉及的知識點有Android屬性動畫,Paint的getSegment()函數。

1.分析動畫

首先,我們來分析一下這個動畫,如上圖所示,我們可以看到,先是圓自己運行閉合,然後在在圓的裏面畫一個✔,對於圓來說,很簡單,無外乎定位圓心與半徑就行,難點在於✔的設計,所以我們先用一張圖來分析一下,如下圖所示:

假設圓的半徑爲mRadius,圓心O座標爲(X,Y),我們把對勾的起點放在A點,假設A的座標爲在圓心平行線中間,那麼A的座標就是(X-mRadius/2,Y),同理,我們假設B的座標也在圓心垂直線下中心,那麼B的座標爲(X,Y+mRadius/2),因爲在屏幕中,原點在屏幕的右上角,正方向X軸向右,正方向Y軸向下,所以這裏是加,同理,C點肯定要接近圓的邊緣,所以C的X軸肯定大於mRadius的一半,姑且我們這裏設爲X+mRadius/2,而Y也相對來說在上邊,而Y不能超出圓外,所以我們根據cX = x + r * cos(a * π / 180);cY = y + r * sin(a * π / 180),計算出該X座標在圓圈的座標,小於這個座標的Y軸設置就行,這裏小編C點設置爲(x+mRadius/2,Y-mRadius/3)。

2.自定義View

原理我們分析清楚了,下面就可以自定義View,來實現支付寶支付成功的動畫了,首先,老樣子與刮一刮類似,在Android Studio中間創建一個類繼承View,實現如下三個構造函數:

public class AlipayView extends View {
    public AlipayView(Context context) {
        super(context);
    }

    public AlipayView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public AlipayView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }
}

 接着我們定義成員變量,如下圖所示:

private Paint paint;//畫筆工具
private Path circlePath,dstPath;//圓路徑,截取路徑
private PathMeasure pathMeasure;//計算路徑的參數
private float mCurrentValue;//動畫執行的進度
private int X,Y,mRadius;//圓心座標與半徑

這裏,我們定義了一個畫筆工具,兩個路徑,以及計算路徑個參數的計算器類,以及動畫執行的進度,所有用到的成員變量都在這裏,接着就是初始化我們的AlipayView。
 

public AlipayView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setLayerType(LAYER_TYPE_SOFTWARE,null);//關閉硬件加速
        this.paint=new Paint(Paint.ANTI_ALIAS_FLAG);//初始化畫筆,且設置爲抗鋸齒
        this.paint.setStrokeWidth(4);//設置畫筆寬度
        this.paint.setStyle(Paint.Style.STROKE);//設置描邊
        this.dstPath=new Path();//初始化
        this.circlePath=new Path();//初始化
        this.circlePath.addCircle(X,Y,mRadius,Path.Direction.CW);//順時針畫圓
        //下面三行代碼畫的是勾的路徑,對照上面分析圖
        this.circlePath.moveTo(X-mRadius/2,Y);
        this.circlePath.lineTo(X,Y+mRadius/2);
        this.circlePath.lineTo(X+mRadius/2,Y-mRadius/3);
        this.pathMeasure=new PathMeasure(this.circlePath,false);//不閉合

        ValueAnimator valueAnimator=ValueAnimator.ofFloat(0,2);//這裏分兩段動畫,一段畫圓,一段畫勾
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                mCurrentValue=(float)animation.getAnimatedValue();//獲取動畫進度
                invalidate();//獲取到進度後進行重繪,會調用onDraw(Canvas canvas)
            }
        });
        valueAnimator.setDuration(4000);//每次動畫時間
        valueAnimator.start();//執行動畫
    }

這裏我們初始化View主要做了三件事,第一初始化各個成員變量,其二將圓的路徑與勾的路徑初始化,但並沒有繪製,繪製都在onDraw(Canvas canvas)函數裏面,第三,初始化動畫的參數,第四,獲取路徑的計算器,計算器包含路徑的一些信息(pathMeasure)。

3.onDraw(canvas)繪製動畫

初始化各個成員變量以及動畫參數後,最後就是要將我們的這些信息,繪製到屏幕上去,而自定義View繪製函數是onDraw(Canvas),代碼如下:

private boolean mNext=false;//判斷是否指閉合
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawColor(Color.WHITE);//首先繪製背景色爲白色
        if(this.mCurrentValue<1){
            //初始化函數中說過,mCurrentValue是記錄動畫的進度的,而小編將動畫進度設置爲2,(0,1)就是畫圓圈,(1,2)就是畫對勾
            //而且mCurrentValue的進度是隨機的,並不一定獲取到1,所以別拿等等與1來計算,只要大於1就執行畫對勾就行
            float stop=this.pathMeasure.getLength()*this.mCurrentValue;//計算當前進度下路徑的百分比,比如,0.25畫到圓的4分之一,那麼整個圓繪畫進度長度就在這裏計算得到。
            //前面說過dstPath是截取路徑,比如一張圖片長200,我截取一半就有100,同樣通過pathMeasure.getSegment就可以截取circlePath的當前進度路徑。
            this.pathMeasure.getSegment(0,stop,dstPath,true);
        }else{
            if(!mNext){
                this.mNext=true;
                //剛說過了,動畫的進度值並不一定會獲取到1,有可能直接從0.99跳到1.01,那麼沒繪製完成的部分,就需要繪製先繪製完成
                this.pathMeasure.getSegment(0,this.pathMeasure.getLength(),dstPath,true);
                this.pathMeasure.nextContour();//因爲圓與對勾並沒有閉合,所以算兩個路徑,這句代碼就是切換到對勾路徑上
            }
            float stop=this.pathMeasure.getLength()*(this.mCurrentValue-1);//每條進度都是按1算百分比的, 但動畫設置的是2,所以減去圓的1,單獨計算勾的路徑百分比
            this.pathMeasure.getSegment(0,stop,dstPath,true);
        }
        canvas.drawPath(dstPath,paint);//把截取到的路徑畫出來
    }

這段首先繪製自定義控件的背景爲白色,然後判斷動畫的進度,如果小於1,執行圓的生成動畫,通過PathMeasure.getSegment將截取的路徑存儲到dstPath中,而當動畫進度不小於1的時候, 因爲動畫的插值器並不一定會獲取到1這個值,所以接下來首先做的就是先閉合這個圓,然後通過next.Contour()切換到勾的路徑,在根據動畫的值繪製勾出來,最後調用canvas將截取的路徑畫到自定義View上面,也就是屏幕上。

private int X=500,Y=500,mRadius=250;//圓心座標

當然照搬上面代碼肯定什麼都沒有,因爲X,Y,mRaduis,都沒有賦值,所以根據自己的需求調整大小,或者設置一個函數,讓外部調用設置靈活的X,Y,半徑都行,代碼如上。

XML佈局文件代碼如下:

<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.liyuanjinglyj.alipayapplication.AlipayView
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

本文代碼Github下載地址:點擊下載

 

發佈了109 篇原創文章 · 獲贊 144 · 訪問量 105萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章