转载请注明出处:http://blog.csdn.net/farmer_cc/article/details/18259117
Android 动画animation 深入分析
前言:本文试图通过分析动画流程,来理解android动画系统的设计与实现,学习动画的基本原则,最终希望能够指导动画的设计。
0 本文中用到的一些类图
1 view animation
调用方法:view.startAnimation(animation);
[java] view plaincopy
- public void startAnimation(Animation animation) {
- animation.setStartTime(Animation.START_ON_FIRST_FRAME);
- setAnimation(animation);
- invalidateParentCaches();
- invalidate(true);
- }
在invalidate(ture);中
[java] view plaincopy
- 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;
[java] view plaincopy
- @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;
- }
[java] view plaincopy
- 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; 是动画系统中的核心组织者, 负责统一调度。后面详细说。
[java] view plaincopy
- final TraversalRunnable mTraversalRunnable = new TraversalRunnable();
- final class TraversalRunnable implements Runnable {
- @Override
- public void run() {
- doTraversal();
- }
- }
[java] view plaincopy
- void doTraversal() {
- performTraversals();
- }
perform 待补充
[java] view plaincopy
- final class ConsumeBatchedInputRunnable implements Runnable {
- @Override
- public void run() {
- doConsumeBatchedInput(mChoreographer.getFrameTimeNanos());
- }
- }
- final ConsumeBatchedInputRunnable mConsumedBatchedInputRunnable =
- new ConsumeBatchedInputRunnable();
doConsume 待补充
2 属性动画aninmator
valueAnimator.start();
[java] view plaincopy
- 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中.
[java] view plaincopy
- getOrCreateAnimationHandler();
[java] view plaincopy
- protected static ThreadLocal<AnimationHandler> sAnimationHandler =
- new ThreadLocal<AnimationHandler>()
- protected static class AnimationHandler implements Runnable {
- // The per-thread list of all active animations
- /** @hide */
- protected final ArrayList<ValueAnimator> mAnimations = new ArrayList<ValueAnimator>();
- // Used in doAnimationFrame() to avoid concurrent modifications of mAnimations
- private final ArrayList<ValueAnimator> mTmpAnimations = new ArrayList<ValueAnimator>();
- // The per-thread set of animations to be started on the next animation frame
- /** @hide */
- protected final ArrayList<ValueAnimator> mPendingAnimations = new ArrayList<ValueAnimator>();
- /**
- * Internal per-thread collections used to avoid set collisions as animations start and end
- * while being processed.
- * @hide
- */
- protected final ArrayList<ValueAnimator> mDelayedAnims = new ArrayList<ValueAnimator>();
- private final ArrayList<ValueAnimator> mEndingAnims = new ArrayList<ValueAnimator>();
- private final ArrayList<ValueAnimator> mReadyAnims = new ArrayList<ValueAnimator>();
- private final Choreographer mChoreographer;
- private boolean mAnimationScheduled;
AnimationHandler 就是一个runnable, 注意成员变量中的多个animator 的list 以及重要的mChoreographer = Choreographer.getInstance();
mChoreographer 也是一个threadlocal的变量。
在animationHandler.start() 中
[java] view plaincopy
- public void start() {
- scheduleAnimation();
- }
- private void scheduleAnimation() {
- if (!mAnimationScheduled) {
- mChoreographer.postCallback(Choreographer.CALLBACK_ANIMATION, this, null);
- mAnimationScheduled = true;
- }
- }
this 是runnable 即把animationHandler自己添加添加到mChoreographer 的队列中。
[java] view plaincopy
- 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);
[java] view plaincopy
- 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);
- }
- }
- }
[java] view plaincopy
- private static final boolean USE_VSYNC = SystemProperties.getBoolean(
- "debug.choreographer.vsync", true);
USE_VSYNC 默认是true;
[java] view plaincopy
- private boolean isRunningOnLooperThreadLocked() {
- return Looper.myLooper() == mLooper;
- }
检查当前looper和mChoreographer的looper是否一致。一般情况是一致的。就会调用scheduleVsyncLocked();
[java] view plaincopy
- private void scheduleVsyncLocked() {
- mDisplayEventReceiver.scheduleVsync();
- }
[java] view plaincopy
- 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方法。
[java] view plaincopy
- // Called by the Choreographer.
- @Override
- public void run() {
- mAnimationScheduled = false;
- doAnimationFrame(mChoreographer.getFrameTime());
- }
[java] view plaincopy
- 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;
[java] view plaincopy
- 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<ValueAnimator> pendingCopy =
- (ArrayList<ValueAnimator>) 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();
[java] view plaincopy
- 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,调用之。
[java] view plaincopy
- 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] 值域范围。
[java] view plaincopy
- 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
从图可以看到,先加速后减速,病最终到达结束位置。
[java] view plaincopy
- 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
如图示,逐渐加速到结束位置。
[java] view plaincopy
- 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 没啥说的。
[java] view plaincopy
- 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)
如图示, 会先反方向执行一段,然后正向一直加速至结束位置。
[java] view plaincopy
- 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
通过下图可以看到,动画会先反方向执行,然后向正方向逐渐加速,在快结束时逐渐减速,并超过预设的值,最后回到结束位置。
2x*x[(2x*(a+1)-a)] 0<=x<=0.5 的函数图
2(x-1)(x-1)[(2x-1)(a+1)+a] 0.5<x<=1的函数图
[java] view plaincopy
- 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节中可以看到,想要让动画按照我们预期的行为来执行,需要做的就是找到合适的函数。
画图使用http://www.fooplot.com/在线工具