上一篇文章:整体结构
了解了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的主要流程就是这样。