作為菜逼,第一次嘗試做動畫。動畫的需求是模仿支付寶付款驗密成功的對號動畫。
主View的代碼:
package gt.research.test.androidtest;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import android.widget.ImageView;
public class TickView extends ImageView {public static final int FRAME =
16;
public static final int INIT =
0;
public static final int DIRTY =
1;
public static final int DRAWN =
2;
public static final int DONE =
3;
private static final int sMARGIN =
5;
private Paint mPaint;
private float mMiddleX;
private float mStartX;
private float mEndX;
private float mBaseY;
private int mOffset;
private int mTickPaddingLeft;
private int mTickPaddingRight;
private int mTickPaddingBottom;
private static final int sDEFAULT_COLOR = Color.parseColor(
"#00307f");
private static final int sDEFAULT_STROCK =
10;
private static int sDEFAULT_DURATION =
100;
private static int sDEFAULT_PADDING =
20;
private volatile float mX;
private volatile float mY;
private float mB0;
private float mB1;
private float mStep;
private long mDuration;
private long mDelay = FRAME;
private volatile int mState = INIT;
private Handler mHandler;
private volatile Rect mDirty;
private Runnable mPostInvalidate =
new Runnable() {
@Overridepublic void run() {
if (DRAWN == mState) {mDirty = getDirtyRect(mX, mY);
if (
null == mDirty) {mState = DONE;
return;}invalidate(mDirty);}
if (INIT == mState || DRAWN == mState || DIRTY == mState) {mHandler.removeCallbacks(mPostInvalidate);mHandler.postDelayed(mPostInvalidate, mDelay);
if (DRAWN == mState) {mState = DIRTY;}}}};
public TickView(Context context) {
super(context);init(
null,
0);}
public TickView(Context context, AttributeSet attrs) {
super(context, attrs);init(attrs,
0);}
public TickView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);init(attrs, defStyle);}
private void init(AttributeSet attrs,
int defStyle) {mPaint =
new Paint();TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.TickView);mPaint.setColor(a.getColor(R.styleable.TickView_tickColor, sDEFAULT_COLOR));mPaint.setStrokeWidth(a.getDimensionPixelSize(R.styleable.TickView_strokeWidth, sDEFAULT_STROCK));mOffset = a.getDimensionPixelOffset(R.styleable.TickView_turnOffset,
0);mDuration = a.getInt(R.styleable.TickView_duration, sDEFAULT_DURATION);mTickPaddingLeft = a.getDimensionPixelSize(R.styleable.TickView_tickPaddingLeft, sDEFAULT_PADDING);mTickPaddingRight = a.getDimensionPixelSize(R.styleable.TickView_tickPaddingRight, sDEFAULT_PADDING);mTickPaddingBottom = a.getDimensionPixelSize(R.styleable.TickView_tickPaddingBottom, sDEFAULT_COLOR);mHandler =
new Handler(Looper.getMainLooper());a.recycle();}
@Overrideprotected void onMeasure(
int widthMeasureSpec,
int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);mMiddleX = getMeasuredWidth() /
2 - mOffset;mX = mStartX = mTickPaddingLeft;mEndX = getMeasuredWidth() - mTickPaddingRight;mBaseY = getMeasuredHeight() - mTickPaddingBottom;mB0 = mBaseY - mMiddleX;mB1 = mBaseY + mMiddleX;mY = mX + mB0;mStep = (mEndX - mStartX) / (
float) mDuration * FRAME;setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec), MeasureSpec.getSize(heightMeasureSpec));}
public void startAnimation() {mHandler.post(mPostInvalidate);mState = INIT;}
@Overrideprotected void onDraw(Canvas canvas) {
if (DONE == mState) {
super.onDraw(canvas);drawTotal(canvas);
return;}
long start = System.currentTimeMillis();
if (canvas.isHardwareAccelerated()) {
super.onDraw(canvas);drawWhole(canvas);}
else {
if (
null != mDirty) {canvas.clipRect(mDirty);}
super.onDraw(canvas);drawPartial(canvas);}mState = DRAWN;
long end = System.currentTimeMillis();mDelay = FRAME - end + start;mDelay = mDelay <
0 ?
0 : mDelay;}
private void drawWhole(Canvas canvas) {
if (mX + mStep < mMiddleX) {canvas.drawLine(mStartX, mStartX + mB0, mX + mStep + sMARGIN, mY + mStep + sMARGIN, mPaint);mX += mStep;mY += mStep;}
else {canvas.drawLine(mStartX, mStartX + mB0, mMiddleX + sMARGIN, mBaseY + sMARGIN, mPaint);canvas.drawLine(mBaseY - mB0 - sMARGIN, mBaseY + sMARGIN, mX + sMARGIN, mB1 - mX - sMARGIN, mPaint);mX += mStep;mY -= mStep;}}
private void drawPartial(Canvas canvas) {
if (mX + mStep < mMiddleX) {canvas.drawLine(mX - sMARGIN, mY - sMARGIN, mX + mStep + sMARGIN, mY + mStep + sMARGIN, mPaint);mX += mStep;mY += mStep;}
else if (mX > mMiddleX) {canvas.drawLine(mX - sMARGIN, mY + sMARGIN, mX + mStep + sMARGIN, mY - mStep - sMARGIN, mPaint);mX += mStep;mY -= mStep;}
else {canvas.drawLine(mX - sMARGIN, mY - sMARGIN, mBaseY - mB0 + sMARGIN, mBaseY + sMARGIN, mPaint);canvas.drawLine(mBaseY - mB0 - sMARGIN, mBaseY + sMARGIN, mX + mStep + sMARGIN, mB1 - mX - mStep - sMARGIN, mPaint);mX += mStep;mY = mB1 - mX;}}
private void drawTotal(Canvas canvas) {canvas.drawLine(mStartX, mStartX + mB0, mMiddleX + sMARGIN, mBaseY + sMARGIN, mPaint);canvas.drawLine(mBaseY - mB0 - sMARGIN, mBaseY + sMARGIN, mEndX, mB1 - mEndX, mPaint);}
private Rect
getDirtyRect(
float x,
float y) {
if (x >= mEndX) {
return null;}Rect rect =
new Rect();
if (x + mStep < mMiddleX) {rect.left = (
int) x - sMARGIN;rect.right = (
int) (x + mStep) + sMARGIN;rect.top = (
int) y - sMARGIN;rect.bottom = (
int) (y + mStep) + sMARGIN;}
else if (x > mMiddleX) {rect.left = (
int) x - sMARGIN;rect.right = (
int) (x + mStep) + sMARGIN;rect.top = (
int) (y - mStep) - sMARGIN;rect.bottom = (
int) y + sMARGIN;}
else {rect.left = (
int) x - sMARGIN;rect.right = (
int) (x + mStep) + sMARGIN;rect.top = (
int) Math.min(y, mB1 - x - mStep) - sMARGIN;rect.bottom = (
int) mBaseY + sMARGIN;}
return rect;}
}
大體思路是,外圍圓圈是背景圖片,中間對勾使用drawLine漸變繪出。
有幾個坑:
- 硬件加速的View不會對某塊DirtyRect重繪,而是全局重繪參考
- draw時使用的是int,需要在轉化時加上margin,避免強轉時露點。margin與ppi相關,基本xhdpi就是1,xx是2
- onDraw時postInvalidate貌似不能用
總結
以上是生活随笔為你收集整理的对号动画实现的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。