Android 動畫animation 深入分析
前言:本文試圖通過分析動畫流程,來理解android動畫系統的設計與實現,學習動畫的基本原則,最終希望能夠指導動畫的設計。
0 本文中用到的一些類圖
1 view animation
調用方法:view.startAnimation(animation);
public void startAnimation(Animation animation) { animation.setStartTime(Animation.START_ON_FIRST_FRAME); setAnimation(animation); invalidateParentCaches(); invalidate(true); }
在invalidate(ture);中
if (p != null && ai != null) { final Rect r = ai.mTmpInvalRect; r.set(0, 0, mRight - mLeft, mBottom - mTop); // Don't call invalidate -- we don't want to internally scroll // our own bounds p.invalidateChild(this, r); }
即調用parent的invalidateChild,
假定父控件即為ViewRootImpl;
public final class ViewRootImpl implements ViewParent;
@Override public void invalidateChild(View child, Rect dirty) { invalidateChildInParent(null, dirty); } public ViewParent invalidateChildInParent(int[] location, Rect dirty) { //...省略一堆判斷條件,最終調用 if (!mWillDrawSoon && (intersected || mIsAnimating)) { scheduleTraversals(); } return null; }
void scheduleTraversals() { if (!mTraversalScheduled) { mTraversalScheduled = true; mTraversalBarrier = mHandler.getLooper().postSyncBarrier(); mChoreographer.postCallback( Choreographer.CALLBACK_TRAVERSAL, mTraversalRunnable, null); scheduleConsumeBatchedInput(); } }
其中mTraversalBarrier = mHandler.getLooper().postSyncBarrier();是設置同步障礙(syncBarrier),當looper中的消息隊列執行到barrier 後,會暫停執行,隻有當barrier 被釋放mHandler.getLooper().removeSyncBarrier(mTraversalBarrier); 後消息隊列才能繼續執行。
Choreographer mChoreographer; 是動畫系統中的核心組織者, 負責統一調度。後面詳細說。
final TraversalRunnable mTraversalRunnable = new TraversalRunnable(); final class TraversalRunnable implements Runnable { @Override public void run() { doTraversal(); } }
void doTraversal() { performTraversals(); }
perform 待補充
final class ConsumeBatchedInputRunnable implements Runnable { @Override public void run() { doConsumeBatchedInput(mChoreographer.getFrameTimeNanos()); } } final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable = new ConsumeBatchedInputRunnable();
doConsume 待補充
2 屬性動畫aninmator
valueAnimator.start();
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException(Animators may only be run on Looper threads); } AnimationHandler animationHandler = getOrCreateAnimationHandler(); animationHandler.mPendingAnimations.add(this); if (mStartDelay == 0) { // This sets the initial value of the animation, prior to actually starting it running setCurrentPlayTime(0); mPlayingState = STOPPED; mRunning = true; notifyStartListeners(); } animationHandler.start(); }
這裡會檢查調用線程必須是Looper線程,如果是view相關的屬性動畫,還必須是UI 線程。
得到AnimationHandle 並把自己加入到PendingAnimations 的list中.
getOrCreateAnimationHandler();
protected static ThreadLocal sAnimationHandler = new ThreadLocal() protected static class AnimationHandler implements Runnable { // The per-thread list of all active animations /** @hide */ protected final ArrayList mAnimations = new ArrayList(); // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations private final ArrayList mTmpAnimations = new ArrayList(); // The per-thread set of animations to be started on the next animation frame /** @hide */ protected final ArrayList mPendingAnimations = new ArrayList(); /** * Internal per-thread collections used to avoid set collisions as animations start and end * while being processed. * @hide */ protected final ArrayList mDelayedAnims = new ArrayList(); private final ArrayList mEndingAnims = new ArrayList(); private final ArrayList mReadyAnims = new ArrayList(); private final Choreographer mChoreographer; private boolean mAnimationScheduled; }
AnimationHandler 就是一個runnable, 註意成員變量中的多個animator 的list 以及重要的mChoreographer = Choreographer.getInstance();
mChoreographer 也是一個threadlocal的變量。
在animationHandler.start() 中
public void start() { scheduleAnimation(); } private void scheduleAnimation() { if (!mAnimationScheduled) { mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null); mAnimationScheduled = true; } }
this 是runnable 即把animationHandler自己添加添加到mChoreographer 的隊列中。
public void postCallback(int callbackType, Runnable action, Object token) { postCallbackDelayed(callbackType, action, token, 0); } public void postCallbackDelayed(int callbackType, Runnable action, Object token, long delayMillis) { postCallbackDelayedInternal(callbackType, action, token, delayMillis); } private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { synchronized (mLock) { final long now = SystemClock.uptimeMillis(); final long dueTime = now + delayMillis; mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token); if (dueTime <= now) { scheduleFrameLocked(now); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_CALLBACK, action); msg.arg1 = callbackType; msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, dueTime); } } }
傳入的delay為0, 即調用scheduleFrameLocked(now);
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) { if (DEBUG) { Log.d(TAG, Scheduling next frame on vsync.); } // If running on the Looper thread, then schedule the vsync immediately, // otherwise post a message to schedule the vsync from the UI thread // as soon as possible. if (isRunningOnLooperThreadLocked()) { scheduleVsyncLocked(); } else { Message msg = mHandler.obtainMessage(MSG_DO_SCHEDULE_VSYNC); msg.setAsynchronous(true); mHandler.sendMessageAtFrontOfQueue(msg); } } else { final long nextFrameTime = Math.max( mLastFrameTimeNanos / NANOS_PER_MS + sFrameDelay, now); if (DEBUG) { Log.d(TAG, Scheduling next frame in + (nextFrameTime - now) + ms.); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } }
private static final boolean USE_VSYNC = SystemProperties.getBoolean( debug.choreographer.vsync, true);
USE_VSYNC 默認是true;
private boolean isRunningOnLooperThreadLocked() { return Looper.myLooper() == mLooper; }
檢查當前looper和mChoreographer的looper是否一致。一般情況是一致的。就會調用scheduleVsyncLocked();
private void scheduleVsyncLocked() { mDisplayEventReceiver.scheduleVsync(); }
public void scheduleVsync() { if (mReceiverPtr == 0) { Log.w(TAG, Attempted to schedule a vertical sync pulse but the display event + receiver has already been disposed.); } else { nativeScheduleVsync(mReceiverPtr); } }
到瞭native 暫時先不涉及。
回頭來看animationHandler 的run()。 前面提到animationHandler把自己添加到mChoreographer,當被調用時,調用run方法。
// Called by the Choreographer. @Override public void run() { mAnimationScheduled = false; doAnimationFrame(mChoreographer.getFrameTime()); }
public long getFrameTime() { return getFrameTimeNanos() / NANOS_PER_MS; } public long getFrameTimeNanos() { synchronized (mLock) { if (!mCallbacksRunning) { throw new IllegalStateException(This method must only be called as + part of a callback while a frame is in progress.); } return USE_FRAME_TIME ? mLastFrameTimeNanos : System.nanoTime(); } }
doAnimationFrame()總結就是
1.遍歷pending list動畫,如果delay為0 則調用start,不為0,加入delay list;
2.遍歷delay list, 根據frametime計算是繼續delay還是ready可以播放,若是ready,則加入到ready list中;
3 遍歷ready list,調用start ;
4,遍歷所有animation,根據frametime計算動畫是否要結束,如果可以結束,則加入到ending list中;
5,遍歷ending list, 調用end;
6, 如果有列表中仍然有動畫,則繼續scheduleAnimation;
private void doAnimationFrame(long frameTime) { // mPendingAnimations holds any animations that have requested to be started // We're going to clear mPendingAnimations, but starting animation may // cause more to be added to the pending list (for example, if one animation // starting triggers another starting). So we loop until mPendingAnimations // is empty. while (mPendingAnimations.size() > 0) { ArrayList pendingCopy = (ArrayList) mPendingAnimations.clone(); mPendingAnimations.clear(); int count = pendingCopy.size(); for (int i = 0; i < count; ++i) { ValueAnimator anim = pendingCopy.get(i); // If the animation has a startDelay, place it on the delayed list if (anim.mStartDelay == 0) { anim.startAnimation(this); } else { mDelayedAnims.add(anim); } } } // Next, process animations currently sitting on the delayed queue, adding // them to the active animations if they are ready int numDelayedAnims = mDelayedAnims.size(); for (int i = 0; i < numDelayedAnims; ++i) { ValueAnimator anim = mDelayedAnims.get(i); if (anim.delayedAnimationFrame(frameTime)) { mReadyAnims.add(anim); } } int numReadyAnims = mReadyAnims.size(); if (numReadyAnims > 0) { for (int i = 0; i < numReadyAnims; ++i) { ValueAnimator anim = mReadyAnims.get(i); anim.startAnimation(this); anim.mRunning = true; mDelayedAnims.remove(anim); } mReadyAnims.clear(); } // Now process all active animations. The return value from animationFrame() // tells the handler whether it should now be ended int numAnims = mAnimations.size(); for (int i = 0; i < numAnims; ++i) { mTmpAnimations.add(mAnimations.get(i)); } for (int i = 0; i < numAnims; ++i) { ValueAnimator anim = mTmpAnimations.get(i); if (mAnimations.contains(anim) && anim.doAnimationFrame(frameTime)) { mEndingAnims.add(anim); } } mTmpAnimations.clear(); if (mEndingAnims.size() > 0) { for (int i = 0; i < mEndingAnims.size(); ++i) { mEndingAnims.get(i).endAnimation(this); } mEndingAnims.clear(); } // If there are still active or delayed animations, schedule a future call to // onAnimate to process the next frame of the animations. if (!mAnimations.isEmpty() || !mDelayedAnims.isEmpty()) { scheduleAnimation(); } }
在animationFrame() 中根據當前狀態,並且計算fraction,調用animateValue();
boolean animationFrame(long currentTime) { boolean done = false; switch (mPlayingState) { case RUNNING: case SEEKED: //省略計算fraction的代碼 animateValue(fraction); break; } return done; }
通過mInterpolator.getInterpolation計算fraction;@Interpolator
根據fraction計算內部所有value,如果有updateListener,調用之。
void animateValue(float fraction) { fraction = mInterpolator.getInterpolation(fraction); mCurrentFraction = fraction; int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].calculateValue(fraction); } if (mUpdateListeners != null) { int numListeners = mUpdateListeners.size(); for (int i = 0; i < numListeners; ++i) { mUpdateListeners.get(i).onAnimationUpdate(this); } } }
3. 插值器
從上面的介紹可以看到,Interpolator的關鍵是getInterpolation();
在ValueAnimator.animationFrame()中可以看到, 傳遞給Interpolator 的fraction是在[0,1] 值域范圍。
float fraction = mDuration > 0 ? (float)(currentTime - mStartTime) / mDuration : 1f; if (fraction >= 1f) { if (mCurrentIteration < mRepeatCount || mRepeatCount == INFINITE) { // Time to repeat if (mListeners != null) { int numListeners = mListeners.size(); for (int i = 0; i < numListeners; ++i) { mListeners.get(i).onAnimationRepeat(this); } } if (mRepeatMode == REVERSE) { mPlayingBackwards = !mPlayingBackwards; } mCurrentIteration += (int)fraction; fraction = fraction % 1f; mStartTime += mDuration; } else { done = true; fraction = Math.min(fraction, 1.0f); } } if (mPlayingBackwards) { fraction = 1f - fraction; }
所以設計Interpolator 就是設計一個輸入[0,1] 的函數。
先參觀一下系統的幾個Interpolator。
3.1 AccelerateDecelerateInterpolator
cos(t+1)Pi /2 +0.5f
從圖可以看到,先加速後減速,病最終到達結束位置。
public class AccelerateDecelerateInterpolator implements Interpolator { public float getInterpolation(float input) { return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f; } }
3.2 AccelerateInterpolator
如果factor=1 則函數為x^2
否則函數為x^a (a 是參數)
默認函數式x^2
如圖示,逐漸加速到結束位置。
public class AccelerateInterpolator implements Interpolator { private final float mFactor; private final double mDoubleFactor; public AccelerateInterpolator() { mFactor = 1.0f; mDoubleFactor = 2.0; } /** * Constructor * * @param factor Degree to which the animation should be eased. Seting * factor to 1.0f produces a y=x^2 parabola. Increasing factor above * 1.0f exaggerates the ease-in effect (i.e., it starts even * slower and ends evens faster) */ public AccelerateInterpolator(float factor) { mFactor = factor; mDoubleFactor = 2 * mFactor; } public float getInterpolation(float input) { if (mFactor == 1.0f) { return input * input; } else { return (float)Math.pow(input, mDoubleFactor); } } }
3.3 LinearInterpolator
線性的就是Y=X 沒啥說的。
public class LinearInterpolator implements Interpolator { public float getInterpolation(float input) { return input; } }
3.4 anticipateInterpolator
函數是:x^2((a+1)x-a) 默認參數a=2 默認函數為x^2(3x-1)
如圖示, 會先反方向執行一段,然後正向一直加速至結束位置。
public class AnticipateInterpolator implements Interpolator { private final float mTension; public AnticipateInterpolator() { mTension = 2.0f; } /** * @param tension Amount of anticipation. When tension equals 0.0f, there is * no anticipation and the interpolator becomes a simple * acceleration interpolator. */ public AnticipateInterpolator(float tension) { mTension = tension; } public float getInterpolation(float t) { // a(t) = t * t * ((tension + 1) * t - tension) return t * t * ((mTension + 1) * t - mTension); } }
3.5 aniticipateOvershoot
是一個分段函數,默認參數a=3
2x*x[(2x*(a+1)-a)] 0<=x<=0.5
2(x-1)(x-1)[(2x-1)(a+1)+a] 0.5<x<=1
</x<=1
通過下圖可以看到,動畫會先反方向執行,然後向正方向逐漸加速,在快結束時逐漸減速,並超過預設的值,最後回到結束位置。
2x*x[(2x*(a+1)-a)] 0<=x<=0.5 的函數圖
2(x-1)(x-1)[(2x-1)(a+1)+a] 0.5<x<=1的函數圖
</x<=1的函數圖
public class AnticipateOvershootInterpolator implements Interpolator { private final float mTension; public AnticipateOvershootInterpolator() { mTension = 2.0f * 1.5f; } /** * @param tension Amount of anticipation/overshoot. When tension equals 0.0f, * there is no anticipation/overshoot and the interpolator becomes * a simple acceleration/deceleration interpolator. */ public AnticipateOvershootInterpolator(float tension) { mTension = tension * 1.5f; } /** * @param tension Amount of anticipation/overshoot. When tension equals 0.0f, * there is no anticipation/overshoot and the interpolator becomes * a simple acceleration/deceleration interpolator. * @param extraTension Amount by which to multiply the tension. For instance, * to get the same overshoot as an OvershootInterpolator with * a tension of 2.0f, you would use an extraTension of 1.5f. */ public AnticipateOvershootInterpolator(float tension, float extraTension) { mTension = tension * extraTension; } private static float a(float t, float s) { return t * t * ((s + 1) * t - s); } private static float o(float t, float s) { return t * t * ((s + 1) * t + s); } public float getInterpolation(float t) { // a(t, s) = t * t * ((s + 1) * t - s) // o(t, s) = t * t * ((s + 1) * t + s) // f(t) = 0.5 * a(t * 2, tension * extraTension), when t < 0.5 // f(t) = 0.5 * (o(t * 2 - 2, tension * extraTension) + 2), when t <= 1.0 if (t < 0.5f) return 0.5f * a(t * 2.0f, mTension); else return 0.5f * (o(t * 2.0f - 2.0f, mTension) + 2.0f); } }
4. 指導設計動畫。
從第3節中可以看到,想要讓動畫按照我們預期的行為來執行,需要做的就是找到合適的函數。