Android使用Path仿支付寶支付成功失敗動畫

24K純帥豆發表於2017-10-19

序言:最近空閒的時候一直在學習自定義View的相關知識,這也是LZ最近半年的學習物件,有的時候就是要給自己定下一個小目標,我們們沒有王老闆的先賺他一個億這麼豪氣,也得先有個目標不是。逛部落格的時候看到支付寶支付成功失敗的動畫效果,剛好最近在學習Path的相關知識,就想著實踐一下,也鞏固一下自己所學的知識,話不多說直接上圖。

這個也是公司專案中需要的,之前由於專案緊,直接讓UI切了個圖,就這樣上了,這不太符合我的一貫作風,但是沒辦法>_<

首先我們來分解一下這個動作,首先是一段progressDialog,可以看做是在請求資料等待過程,然後成功之後顯示成功的動畫,失敗之後顯示失敗的動畫,那麼這裡涉及到三個狀態,載入中、載入成功和載入失敗,這裡我們使用列舉來實現這三種狀態。首先呢,我們先來實現這個等待的進度條:

1、畫一個圓,確切的來說是畫一段圓弧,然後旋轉畫布,在此過程中不斷修改圓弧的大小,造成一個這樣動態的假象:

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.translate(getPaddingLeft(), getPaddingTop());   //將當前畫布的點移到getPaddingLeft,getPaddingTop,後面的操作都以該點作為參照點
    if (mStatus == StatusEnum.Loading) {    //正在載入
        if (startAngle == minAngle) {
            sweepAngle += 6;
        }
        if (sweepAngle >= 300 || startAngle > minAngle) {
            startAngle += 6;
            if (sweepAngle > 20) {
                sweepAngle -= 6;
            }
        }
        if (startAngle > minAngle + 300) {
            startAngle %= 360;
            minAngle = startAngle;
            sweepAngle = 20;
        }
        canvas.rotate(curAngle += 4, progressRadius, progressRadius);  //旋轉的弧長為4
        canvas.drawArc(new RectF(0, 0, progressRadius * 2, progressRadius * 2), startAngle, sweepAngle, false, mPaint);
        invalidate();
    }
}複製程式碼

這裡startAngle表示圓弧的起始角度,sweepAngle表示圓弧掃過的角度,minAngle是一個過渡值,是為了幫助startAngle改變值而用到的。這裡用到了畫弧度的方法,在上一篇部落格中我有細講這個方法,如果你還不知道的話請移步Android自定義view之圓形進度條,這裡還用到了rotate方法,來看一下它的原始碼解釋:

/**
 * Preconcat the current matrix with the specified rotation.
 *
 * @param degrees The amount to rotate, in degrees
 * @param px The x-coord for the pivot point (unchanged by the rotation)
 * @param py The y-coord for the pivot point (unchanged by the rotation)
 */
public final void rotate(float degrees, float px, float py) {
    translate(px, py);
    rotate(degrees);
    translate(-px, -py);
}複製程式碼

這個方法主要是將畫布進行旋轉,我們可以看到,先是將畫布平移到某個點,然後再旋轉某個角度,最後再平移回去,這樣做的目的是為了讓需要旋轉的View進行中心對稱旋轉,所以後面傳的PX,PY值需要是View寬高的一半,不信的話你可以去做個實驗;說了這麼多我們直接來看一下效果:

2、畫成功狀態的動畫,這部分也可以分成兩個小部分,先是畫一個圓,然後再畫中間的鉤:
(1)、畫圓:上一篇部落格中講了通過進度來畫弧進而來畫整個圓,今天我們的主角是Path,所以我們使用Path來實現這樣一個效果,還是先上程式碼,通過程式碼來講解:

//追蹤Path的座標
private PathMeasure mPathMeasure;
//畫圓的Path
private Path mPathCircle;
//擷取PathMeasure中的path
private Path mPathCircleDst;

mPaint.setColor(loadSuccessColor);
mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, progressRadius, Path.Direction.CW);
mPathMeasure.setPath(mPathCircle, false);
mPathMeasure.getSegment(0, circleValue * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);複製程式碼

Path的常用方法有,add一條路徑(任意形狀,任意線條),這裡我們在path中新增了一個圓的路徑,具體Path常見的用法如下表所示:

Path的常見方法 方法含義
moveTo() 該方法移動後續操作的起點座標
lineTo() 該方法是連線起始點與某一點(傳的引數)形成一條線
setLastPath() 該方法是設定Path最後的座標
close() 該方法是將起點座標與終點座標連線起來形成一個閉合的圖形(如果始終點左邊能連線的話)
addRect() 該方法是繪製一個巨型
addRoundRect() 該方法是繪製一個圓角矩形
addOval() 該方法是繪製一個橢圓
arcTo() 該方法是繪製一段圓弧
addArc() 該方法是繪製一段圓弧

然後呢,這裡有一個PathMeasure,簡單點說,這玩意就是用來實現Path座標點的追蹤,你也可以認為是Path座標的計算器,具體PathMeasure的常見的用法如下表所示:

PathMeasure的常見方法 方法含義
setPath() 該方法將path與PathMeasure繫結起來
getLength() 該方法用於獲得path路徑的長度
getSegment() 該方法用於擷取整個Path的片段
nextContour() 該方法用於切換到下一個路徑

這裡我們通過動畫從0——1之間的變化,來改變所畫圓的弧度:

circleAnimator = ValueAnimator.ofFloat(0, 1);
circleAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
        circleValue = (float) animation.getAnimatedValue();
        invalidate();
    }
});複製程式碼

(2)、接下來畫完圓之後,我們要開始畫對鉤了:

對鉤
對鉤

if (circleValue == 1) {      //表示圓畫完了,可以鉤了
    successPath.moveTo(getWidth() / 1 * 3, getWidth() / 2);
    successPath.lineTo(getWidth() / 2, getWidth() / 5 * 3);
    successPath.lineTo(getWidth() / 3 * 2, getWidth() / 5 * 2);
    mPathMeasure.nextContour();
    mPathMeasure.setPath(successPath, false);
    mPathMeasure.getSegment(0, successValue * mPathMeasure.getLength(), mPathCircleDst, true);
    canvas.drawPath(mPathCircleDst, mPaint);
}複製程式碼

這裡的座標我是根據UI給的圖大致算出來的,可以參考下面這張圖的虛線和實現,對鉤的起始座標在座標軸中大致是getWidth() / 8 * 3,你也可以根據你的需求來畫出這個對鉤,其實就是兩段路徑,分別用pathlineTo方法來實現:

成功畫對勾
成功畫對勾

同理,畫叉叉也是一樣的,只要你算出叉在座標軸中的座標就ok了,這裡我也給出一張參考圖:

叉叉
叉叉

mPaint.setColor(loadFailureColor);
mPathCircle.addCircle(getWidth() / 2, getWidth() / 2, progressRadius, Path.Direction.CW);
mPathMeasure.setPath(mPathCircle, false);
mPathMeasure.getSegment(0, circleValue * mPathMeasure.getLength(), mPathCircleDst, true);
canvas.drawPath(mPathCircleDst, mPaint);

if (circleValue == 1) {  //表示圓畫完了,可以畫叉叉的右邊部分
    failurePathRight.moveTo(getWidth() / 3 * 2, getWidth() / 3);
    failurePathRight.lineTo(getWidth() / 3, getWidth() / 3 * 2);
    mPathMeasure.nextContour();
    mPathMeasure.setPath(failurePathRight, false);
    mPathMeasure.getSegment(0, failValueRight * mPathMeasure.getLength(), mPathCircleDst, true);
    canvas.drawPath(mPathCircleDst, mPaint);
}

if (failValueRight == 1) {    //表示叉叉的右邊部分畫完了,可以畫叉叉的左邊部分
    failurePathLeft.moveTo(getWidth() / 3, getWidth() / 3);
    failurePathLeft.lineTo(getWidth() / 3 * 2, getWidth() / 3 * 2);
    mPathMeasure.nextContour();
    mPathMeasure.setPath(failurePathLeft, false);
    mPathMeasure.getSegment(0, failValueLeft * mPathMeasure.getLength(), mPathCircleDst, true);
    canvas.drawPath(mPathCircleDst, mPaint);
}複製程式碼

失敗畫叉叉
失敗畫叉叉

參考:

Android自定義之仿支付寶支付成功、失敗狀態的載入進度

自定義View(三)使用Path仿支付寶支付成功效果

PathMeasure之迷徑追蹤

到此就完成了自定義的原型進度條了。原始碼已上傳至Github,有需要的同學可以下載下來看看,歡迎Star,Fork

同時感謝以上引用到部落格的主人,感謝!!!

公眾號:Android技術經驗分享
公眾號:Android技術經驗分享

相關文章