转载请注明出处:http://blog.csdn.net/farmer_cc/article/details/18259117

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

通过下图可以看到,动画会先反方向执行,然后向正方向逐渐加速,在快结束时逐渐减速,并超过预设的值,最后回到结束位置。


2x*x[(2x*(a+1)-a)]     0<=x<=0.5 的函数图



2(x-1)(x-1)[(2x-1)(a+1)+a]    0.5

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/在线工具


更多相关文章

  1. android中关于微信支付流程的总结
  2. Android两个Activity传递数据,onActivityResult获取结果时Intent
  3. Android—Service与Activity的交互
  4. Android(安卓)windowTranslucentStatus属性源码分析
  5. 分析Activity的View绑定过程
  6. android中的常见类(2)
  7. Android的Activity屏幕切换动画
  8. android 视图结构 呈现给用户的视图
  9. android软键盘状态监听最稳的方法,属性动画手动调整布局,再也不怕

随机推荐

  1. Android截图的两种方法
  2. Skype for Business 2015 快速入门手册 v
  3. android下phonegap插件
  4. Android之Service案例-电话
  5. Android(Java):LayoutInflater
  6. Android之文件操作和JUnit
  7. android提供的Log类
  8. Android 通过wifi调试程序
  9. 开源一个友盟 for android 操作的封装包
  10. android SeekBar自定义样式滑动条的使用