1. 程式人生 > >仿支付寶支付成功打勾動畫(關於PathMeasure你該知道的東西)

仿支付寶支付成功打勾動畫(關於PathMeasure你該知道的東西)

Android路徑動畫

沿預定路徑生成動畫影象(無論是直線或曲線)是做動畫時一種常見的情況。傳統方法是尋找Path的函式(貝塞爾函式用的比較多),帶入x的值來計算y的值, 然後使用座標(x,y)來完成動畫。
現在還有一種更簡單的方法:通過使用path和PathMeasure來完成。
Path大家都很熟悉了,那PathMeasure是什麼東西呢,從命名上就可以看出,這相當於Path座標計算器。以前我們需要知道路徑函式的演算法才能算座標,現在PathMeasure幫我們處理了這一過程。現在來講講它的使用方法

初始化

PathMeasure pathMeasure = new PathMeasure();

PathMeasure提供了一個setPath()將path和pathMeasure進行繫結

pathMeasure.setPath(path,false);

當然也可通過初始化的時候就指定:

PathMeasure pathMeasure= new PathMeasure(Path path,boolean forceClosed);

forceClosed決定是否強制閉合,設定為true的話,path首位最尾在measure時會閉合。

forceClosed引數對繫結的Path不會產生任何影響,例如一個折線段的Path,本身是沒有閉合的,forceClosed設定為True的時候,PathMeasure計算的Path是閉合的,但Path本身繪製出來是不會閉合的。

forceClosed引數對PathMeasure的測量結果有影響,還是例如前面說的一個折線段的Path,本身沒有閉合,forceClosed設定為True,PathMeasure的計算就會包含最後一段閉合的路徑,與原來的Path不同。

API提供的其它重要方法

  • float getLength():獲取路徑長度
  • boolean getPosTan(float distance, float[] pos, float[] tan) 傳入一個大於0小於getLength()的獲取座標點和切線的座標,放入post[]和tan[]中
  • boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 傳入一個開始點和結束點, 然後會返回介於這之間的Path,當作dst。startWithMoveTo通常為True,保證每次擷取的Path片段都是正常的、完整的
  • boolean nextContour 當需要傳入多個path的時候進行切換用的,contour時是輪廓的意思

支付動畫簡單版——打勾動畫

PathMeasure到底是怎麼做的呢,我們先以一個支付動畫的簡單版來說明,在這個例項中,要完成的操作就是畫一個圓,在圓圈裡面進行動態打勾。這裡用的的是getSegment不斷繪製path的每一段,直到繪製完成。
怎麼控制進度呢,我們定義了一個tickPercent來標記進度的百分比,通過valueAnimator來實時更新進度來繪製,具體的實現程式碼如下:

public class CircleTickView extends View {


    private PathMeasure tickPathMeasure;
    /**
     * 打鉤百分比
     */
    float tickPercent = 0;

    private Path path;
    //初始化打鉤路徑
    private Path tickPath;


    // 圓圈的大小,半徑
    private int circleRadius;
    private int circleColor;
    private int circleStrokeWidth;

    private RectF rec;
    private Paint tickPaint;


    public CircleTickView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    public CircleTickView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public CircleTickView(Context context) {
        super(context);
        init(context, null);
    }

    public void init(Context context, AttributeSet attrs) {

        TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleTickView);
        circleRadius = mTypedArray.getInteger(R.styleable.CircleTickView_circleRadius, 150);
        circleColor = mTypedArray.getColor(R.styleable.CircleTickView_circleViewColor, ContextCompat.getColor(context, R.color.colorPrimary));
        circleStrokeWidth = mTypedArray.getInteger(R.styleable.CircleTickView_circleStrokeWidth, 20);
        mTypedArray.recycle();

        tickPaint = new Paint();
        tickPathMeasure = new PathMeasure();
        rec = new RectF();
        path = new Path();
        tickPath = new Path();
        tickPaint.setStyle(Paint.Style.STROKE);
        tickPaint.setAntiAlias(true);
        tickPaint.setColor(circleColor);
        tickPaint.setStrokeWidth(circleStrokeWidth);

        //打鉤動畫
        ValueAnimator mTickAnimation;
        mTickAnimation = ValueAnimator.ofFloat(0f, 1f);
        mTickAnimation.setStartDelay(1000);
        mTickAnimation.setDuration(500);
        mTickAnimation.setInterpolator(new AccelerateInterpolator());
        mTickAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                tickPercent = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        mTickAnimation.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {

        int width = canvas.getWidth();
        int height = canvas.getHeight();

        // 根據設定該view的高度,進行對所畫圖進行居中處理
        int offsetHeight = (height - circleRadius * 2) / 2;

        // 設定第一條直線的相關引數
        int firStartX = width / 2 - circleRadius * 3 / 5;
        int firStartY = offsetHeight + circleRadius;

        int firEndX = (width / 2 - circleRadius / 5) - 1;
        int firEndY = offsetHeight + circleRadius + circleRadius / 2 + 1;


        int secEndX = width / 2 + circleRadius * 3 / 5;
        int secEndY = offsetHeight + circleRadius / 2;


        rec.set(width / 2 - circleRadius, offsetHeight, width / 2 + circleRadius, offsetHeight + circleRadius * 2);
        tickPath.moveTo(firStartX, firStartY);
        tickPath.lineTo(firEndX, firEndY);
        tickPath.lineTo(secEndX, secEndY);
        tickPathMeasure.setPath(tickPath, false);
        /*
         * On KITKAT and earlier releases, the resulting path may not display on a hardware-accelerated Canvas.
         * A simple workaround is to add a single operation to this path, such as dst.rLineTo(0, 0).
         */
        tickPathMeasure.getSegment(0, tickPercent * tickPathMeasure.getLength(), path, true);
        path.rLineTo(0, 0);
        canvas.drawPath(path, tickPaint);
        canvas.drawArc(rec, 0, 360, false, tickPaint);
    }


}

想寫這麼多了,有空再把全真的支付寶支付動畫po上來。