ValueAnimator源码——动画驱动

上一篇文章:整体结构

了解了ValueAnimator结构之后,现在看start方法
start方法最终调用重载方法:(参数为false)

private void start(boolean playBackwards) {
        if (Looper.myLooper() == null) {
            throw new AndroidRuntimeException("Animators may only be run on Looper threads");
        }
        mReversing = playBackwards;
        // Special case: reversing from seek-to-0 should act as if not seeked at all.
        if (playBackwards && mSeekFraction != -1 && mSeekFraction != 0) {
            if (mRepeatCount == INFINITE) {
                // Calculate the fraction of the current iteration.
                float fraction = (float) (mSeekFraction - Math.floor(mSeekFraction));
                mSeekFraction = 1 - fraction;
            } else {
                mSeekFraction = 1 + mRepeatCount - mSeekFraction;
            }
        }
        mStarted = true;
        mPaused = false;
        mRunning = false;
        mAnimationEndRequested = false;
        // Resets mLastFrameTime when start() is called, so that if the animation was running,
        // calling start() would put the animation in the
        // started-but-not-yet-reached-the-first-frame phase.
        mLastFrameTime = 0;
        AnimationHandler animationHandler = AnimationHandler.getInstance();
        animationHandler.addAnimationFrameCallback(this, (long) (mStartDelay * sDurationScale));

        if (mStartDelay == 0 || mSeekFraction >= 0) {
            // If there's no start delay, init the animation and notify start listeners right away
            // to be consistent with the previous behavior. Otherwise, postpone this until the first
            // frame after the start delay.
            startAnimation();
            if (mSeekFraction == -1) {
                // No seek, start at play time 0. Note that the reason we are not using fraction 0
                // is because for animations with 0 duration, we want to be consistent with pre-N
                // behavior: skip to the final value immediately.
                setCurrentPlayTime(0);
            } else {
                setCurrentFraction(mSeekFraction);
            }
        }
    }

首先,检查当前线程必须有Looper
然后设置一些标志位
我们先看AnimationHandler后面的

如果mStartDelay为0,也就是没有设置startDelay,就会调用startAnimation
startAnimation会设置mRunning为true,通知listener动画开始了以及一些初始化工作

现在回到AnimationHandler
从AnimationHandler.getInstance()看出,用到了ThreadLocal,所以AnimationHandler是线程单例的
再看后面的那个方法

public void addAnimationFrameCallback(final AnimationFrameCallback callback, long delay) {
        if (mAnimationCallbacks.size() == 0) {
            getProvider().postFrameCallback(mFrameCallback);
        }
        if (!mAnimationCallbacks.contains(callback)) {
            mAnimationCallbacks.add(callback);
        }

        if (delay > 0) {
            mDelayedCallbackStartTime.put(callback, (SystemClock.uptimeMillis() + delay));
        }
    }

参数里的callback就是ValueAnimator自己,delay就是startDelay
这里其实是观察者模式,同一个线程里所有的ValueAnimator都受AnimationHandler管理
mAnimationCallbacks就是一个ArrayList
mDelayedCallbackStartTime是一个map,key是valueAnimator,value是实际开始的时间

那么开头那段代码是什么意思呢?
这段代码最终会调用Choreographer的postFrameCallback(callback)方法
简单来说,Choreographer会在下一个frame被渲染时调用callback,你可以简单理解为过一段时间后会调用callback

来看mFrameCallback
是AnimationHandler的内部类,只有一个方法:

public void doFrame(long frameTimeNanos) {
            doAnimationFrame(getProvider().getFrameTime());
            if (mAnimationCallbacks.size() > 0) {
                getProvider().postFrameCallback(this);
            }
}

后面那一部分的意思是,如果还有注册进来的valueAnimator,那还要继续向Choreographer注册callback

来看doAnimationFrame方法,
会遍历所有注册进来的valueAnimator,如果开始时间到了
(还记得前面,valueAnimator向AnimationHandler注册的时候,AnimationHandler会保存动画开始时间)
那就调用valueAnimator的doAnimationFrame方法,终于回到valueAnimator了

回顾一下,调用valueAnimator的start方法,会设置一些标志位,然后向AnimationHandler注册。
AnimationHandler会保存一些信息,并且会向Choreographer注册自己,这样每隔一段时间都能收到通知。
AnimationHandler被回调后,根据时间找到需要开始的动画,再调用valueAnimator的方法。

回到valueAnimator,看doAnimationFrame方法
如果是第一次开始,会记录开始时间 mStartTime
之后调用animateBasedOnTime方法
这个方法主要是计算出原始的fraction(当前时间减去开始时间再除以总长度),判断是否结束,如果有重复动画,还要把小数部分提取出来,因为传入interpolator的fraction应在0到1

fraction弄好了,最后调用animateValue(float fraction)方法:

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);
            }
        }
    }

这里终于调用了interpolator
mValues就是PropertyValuesHolder数组,一般情况下只会有一个元素
PropertyValuesHolder的calculateValue方法在上一篇讲过,就是让keyframeSet计算出具体的属性值,并处理一下。

最后,看一下动画结束时会调用的endAnimation方法

 AnimationHandler handler = AnimationHandler.getInstance();
        handler.removeCallback(this);

        mAnimationEndRequested = true;
        mPaused = false;
        if ((mStarted || mRunning) && mListeners != null) {
            if (!mRunning) {
                // If it's not yet running, then start listeners weren't called. Call them now.
                notifyStartListeners();
             }
            ArrayList tmpListeners =
                    (ArrayList) mListeners.clone();
            int numListeners = tmpListeners.size();
            for (int i = 0; i < numListeners; ++i) {
                tmpListeners.get(i).onAnimationEnd(this);
            }
        }
        mRunning = false;
        mStarted = false;
        mStartListenersCalled = false;
        mReversing = false;
        mLastFrameTime = 0;
        ........(省略)

首先向AnimationHandler取消注册
如果已经start但还没开始,会通知Listener动画开始和结束
最后设置一下标志位

valueAnimator的主要流程就是这样。

你可能感兴趣的:(Animation)