仿支付寶支付成功打勾動畫(關於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上來。