-
概述
我们知道,补间动画是设置View相关属性值的起止点,然后系统会值变化中自动给View的属性赋值,但是补间动画只能用于View,而且只能设置平移、缩放、旋转、透明度四种属性,这不仅不够灵活而且可变的属性也很少。在这样的背景下,属性动画诞生了,通过它我们可以完全自己把握属性变化过程,但是需要手动设置对象属性以达到动画效果,我们可以把任何对象的任何属性作为动画变化的参照物。
-
创建
可以通过new的方式创建一个ValueAnimator对象,但是有些必须的属性需要手动set,除非你需要完全自定义一个ValueAnimator对象,否则用ValueAnimator的几个静态方法构造即可。
它有一个数组叫mValues,里面存放着PropertyValuesHolder,这个类持有了对应属性的所有关键帧的数据。
ValueAnimator的几个用于构造的静态方法中都会调用PropertyValuesHolder的对应类型的静态方法创建PropertyValuesHolder对象并添加到mValues中,总共有以下5个静态方法:
Integer类型:
public static ValueAnimator ofInt(int... values) { ValueAnimator anim = new ValueAnimator(); anim.setIntValues(values); return anim; }
颜色argb类型(为什么是int后面再说):
public static ValueAnimator ofArgb(int... values) { ValueAnimator anim = new ValueAnimator(); anim.setIntValues(values); anim.setEvaluator(ArgbEvaluator.getInstance()); return anim; }
Float类型:
public static ValueAnimator ofFloat(float... values) { ValueAnimator anim = new ValueAnimator(); anim.setFloatValues(values); return anim; }
直接传入PropertyValuesHolder:
public static ValueAnimator ofPropertyValuesHolder(PropertyValuesHolder... values) { ValueAnimator anim = new ValueAnimator(); anim.setValues(values); return anim; }
通用类型:
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values) { ValueAnimator anim = new ValueAnimator(); anim.setObjectValues(values); anim.setEvaluator(evaluator); return anim; }
这里传入的参数都是可变不定长参数类型,可以注意到,这些方法中setXxxValues的调用方法不同,比如setIntValues方法:
public void setIntValues(int... values) { if (values == null || values.length == 0) { return; } if (mValues == null || mValues.length == 0) { //Code1 setValues(PropertyValuesHolder.ofInt("", values)); } else { PropertyValuesHolder valuesHolder = mValues[0]; valuesHolder.setIntValues(values); } // New property/values/target should cause re-initialization prior to starting mInitialized = false; }
其他几个都是类似的代码,只不过除了参数类型不一样之外,Code1处会换成PropertyValuesHolder的对应静态方法去生成对应的PropertyValuesHolder对象,比如setFloatValues中是PropertyValuesHolder.ofFloat方法。以PropertyValuesHolder.ofInt为例:
public static PropertyValuesHolder ofInt(String propertyName, int... values) { return new IntPropertyValuesHolder(propertyName, values); }
IntPropertyValuesHolder的构造方法中会一直调用PropertyValuesHolder的setIntValues方法:
public void setIntValues(int... values) { mValueType = int.class; mKeyframes = KeyframeSet.ofInt(values); }
这里又调用了KeyframeSet的ofInt方法:
public static KeyframeSet ofInt(int... values) { int numKeyframes = values.length; IntKeyframe keyframes[] = new IntKeyframe[Math.max(numKeyframes,2)]; if (numKeyframes == 1) { keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f); keyframes[1] = (IntKeyframe) Keyframe.ofInt(1f, values[0]); } else { keyframes[0] = (IntKeyframe) Keyframe.ofInt(0f, values[0]); for (int i = 1; i < numKeyframes; ++i) { keyframes[i] = (IntKeyframe) Keyframe.ofInt((float) i / (numKeyframes - 1), values[i]); } } return new IntKeyframeSet(keyframes); }
这里生成一个values长度大小的keyframes数组,再调用Keyframe的ofInt方法生成IntKeyframe对象并添加到keyframes中,最后返回一个持有keyframes的IntKeyframeSet对象。
IntKeyframe(float fraction, int value) { mFraction = fraction; mValue = value; mValueType = int.class; mHasValue = true; }
IntKeyframe继承自Keyframe,这里的mValueType固定为int.class。IntKeyframeSet构造方法只是调用了super的构造方法:
public KeyframeSet(Keyframe... keyframes) { mNumKeyframes = keyframes.length; // immutable list mKeyframes = Arrays.asList(keyframes); mFirstKeyframe = keyframes[0]; mLastKeyframe = keyframes[mNumKeyframes - 1]; mInterpolator = mLastKeyframe.getInterpolator(); }
除此之外,还能发现ofArgb和ofObject方法中还调用了setEvaluator方法传入一个TypeEvaluator类型的对象,这个类是一个接口,定一个一个evaluate方法,这里先记住,后面会用到它。
-
设置监听
属性动画和补间动画的一个根本的不同就是属性动画需要手动根据属性变化即时修改对象的属性,所以我们需要设置监听来在属性变化过程中及时的修改对象的属性。
在父类Animator中有一个AnimatorListener接口,你可以实现onAnimationStart、onAnimationEnd、onAnimationCancel、onAnimationRepeat等接口,你可以通过这个完成在一些临界点时的操作。
若要捕捉在属性变化过程中的值则需要添加ValueAnimator中的AnimatorUpdateListener,它只有一个onAnimationUpdate方法,可以在这里完成对象对应属性值的更新工作。
-
start
监听器设置好了之后就可以调用start方法开启动画了:
private void start(boolean playBackwards) { if (Looper.myLooper() == null) { throw new AndroidRuntimeException("Animators may only be run on Looper threads"); } mReversing = playBackwards; mSelfPulse = !mSuppressSelfPulseRequested; // 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 = -1; mFirstFrameTime = -1; mStartTime = -1; addAnimationCallback(0); if (mStartDelay == 0 || mSeekFraction >= 0 || mReversing) { // 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); } } }
mSeekFraction代表的是进度,mRepeatCount == INFINITE时可以看到调用了Math.floor(mSeekFraction)方法,这个方法会返回正无穷无限接近于mSeekFraction的一个值,然后执行mSeekFraction = 1 - fraction;,这说明mSeekFraction永远不可能到达1,依旧意味着动画永远不会结束。
mStartDelay为0说明到了执行时间了,startAnimation中:
private void startAnimation() { if (Trace.isTagEnabled(Trace.TRACE_TAG_VIEW)) { Trace.asyncTraceBegin(Trace.TRACE_TAG_VIEW, getNameForTrace(), System.identityHashCode(this)); } mAnimationEndRequested = false; initAnimation(); mRunning = true; if (mSeekFraction >= 0) { mOverallFraction = mSeekFraction; } else { mOverallFraction = 0f; } if (mListeners != null) { notifyStartListeners(); } }
这里调用了一个initAnimation方法:
void initAnimation() { if (!mInitialized) { int numValues = mValues.length; for (int i = 0; i < numValues; ++i) { mValues[i].init(); } mInitialized = true; } }
mValues我们知道是什么了,这里会把mValues中的每一个PropertyValuesHolder对象执行init方法:
void init() { if (mEvaluator == null) { // We already handle int and float automatically, but not their Object // equivalents mEvaluator = (mValueType == Integer.class) ? sIntEvaluator : (mValueType == Float.class) ? sFloatEvaluator : null; } if (mEvaluator != null) { // KeyframeSet knows how to evaluate the common types - only give it a custom // evaluator if one has been set on this class mKeyframes.setEvaluator(mEvaluator); } }
mEvaluator!很熟悉对不对,还记得在创建ValueAnimatior的静态构造方法中,有的调用了setEvaluator方法设置了它,为什么int和float类型的构造方法中没有调用setEvaluator方法呢,原因就在这里。
startAnimation方法另外的作用就是通知添加的AnimatorListener执行onAnimationStart方法。
startAnimation结束后会执行setCurrentFraction方法(setCurrentPlayTime方法里也是调用的这个方法):
public void setCurrentFraction(float fraction) { initAnimation(); fraction = clampFraction(fraction); mStartTimeCommitted = true; // do not allow start time to be compensated for jank if (isPulsingInternal()) { long seekTime = (long) (getScaledDuration() * fraction); long currentTime = AnimationUtils.currentAnimationTimeMillis(); // Only modify the start time when the animation is running. Seek fraction will ensure // non-running animations skip to the correct start time. mStartTime = currentTime - seekTime; } else { // If the animation loop hasn't started, or during start delay, the startTime will be // adjusted once the delay has passed based on seek fraction. mSeekFraction = fraction; } mOverallFraction = fraction; final float currentIterationFraction = getCurrentIterationFraction(fraction, mReversing); animateValue(currentIterationFraction); }
fraction表示当前进度位置,clampFraction方法更正fraction范围在[0, mRepeatCount + 1]之间:
private float clampFraction(float fraction) { if (fraction < 0) { fraction = 0; } else if (mRepeatCount != INFINITE) { fraction = Math.min(fraction, mRepeatCount + 1); } return fraction; }
isPulsingInternal()为true表示动画之前已经开始了,修改mStartTime是为了和现在的进度对应,即mStartTime保存的是每一次开始的时间(相对于duration)。最后执行animateValue方法:
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[i].calculateValue:
void calculateValue(float fraction) { Object value = mKeyframes.getValue(fraction); mAnimatedValue = mConverter == null ? value : mConverter.convert(value); }
可以看到,通过mKeyframes的getValue方法获取相对于当前进度的value,这里也就是IntKeyframeSet的getValue方法:
@Override public Object getValue(float fraction) { return getIntValue(fraction); }
也就是getIntValue方法:
@Override public int getIntValue(float fraction) { if (fraction <= 0f) { final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0); final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(1); int prevValue = prevKeyframe.getIntValue(); int nextValue = nextKeyframe.getIntValue(); float prevFraction = prevKeyframe.getFraction(); float nextFraction = nextKeyframe.getFraction(); final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). intValue(); } else if (fraction >= 1f) { final IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 2); final IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(mNumKeyframes - 1); int prevValue = prevKeyframe.getIntValue(); int nextValue = nextKeyframe.getIntValue(); float prevFraction = prevKeyframe.getFraction(); float nextFraction = nextKeyframe.getFraction(); final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); if (interpolator != null) { fraction = interpolator.getInterpolation(fraction); } float intervalFraction = (fraction - prevFraction) / (nextFraction - prevFraction); return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)).intValue(); } IntKeyframe prevKeyframe = (IntKeyframe) mKeyframes.get(0); for (int i = 1; i < mNumKeyframes; ++i) { IntKeyframe nextKeyframe = (IntKeyframe) mKeyframes.get(i); if (fraction < nextKeyframe.getFraction()) { final TimeInterpolator interpolator = nextKeyframe.getInterpolator(); float intervalFraction = (fraction - prevKeyframe.getFraction()) / (nextKeyframe.getFraction() - prevKeyframe.getFraction()); int prevValue = prevKeyframe.getIntValue(); int nextValue = nextKeyframe.getIntValue(); // Apply interpolator on the proportional duration. if (interpolator != null) { intervalFraction = interpolator.getInterpolation(intervalFraction); } return mEvaluator == null ? prevValue + (int)(intervalFraction * (nextValue - prevValue)) : ((Number)mEvaluator.evaluate(intervalFraction, prevValue, nextValue)). intValue(); } prevKeyframe = nextKeyframe; } // shouldn't get here return ((Number)mKeyframes.get(mNumKeyframes - 1).getValue()).intValue(); }
可以看到这里会判断mEvaluator是否为null,说明int和float也可以设置Evaluator,如果设置了则以Evaluator优先,mEvaluator会调用evaluate方法来计算新的值,这里会直接强转成int类型返回,Object类型构造的是KeyframeSet,它的就不会强转:
public Object getValue(float fraction) { // Special-case optimization for the common case of only two keyframes if (mNumKeyframes == 2) { if (mInterpolator != null) { fraction = mInterpolator.getInterpolation(fraction); } return mEvaluator.evaluate(fraction, mFirstKeyframe.getValue(), mLastKeyframe.getValue()); } ... ... }
calculateValue最后会判断mConverter是否为空来对fraction作进一步转化,最终赋值给mAnimatedValue,mConverter通过setConverter方法设置,在ofObject的其中一个重载方法中会调用,你可以在此传入,这是可选项。
回到animateValue,最后会执行所有监听器的onAnimationUpdate方法,这就会回到我们自定义的属性更新逻辑中去了。
-
Choreographer刷新
前面的已经把结合关键帧数据的计算步骤和更新步骤都做好了,start只会更新一次(即初始值),那么谁来触发更新这个进度呢?
在ValueAnimator的start方法中startAnimation之前有一句addAnimationCallback(0):
private void addAnimationCallback(long delay) { if (!mSelfPulse) { return; } getAnimationHandler().addAnimationFrameCallback(this, delay); }
addAnimationFrameCallback如下:
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)); } }
mFrameCallback是:
private final Choreographer.FrameCallback mFrameCallback = new Choreographer.FrameCallback() { @Override public void doFrame(long frameTimeNanos) { doAnimationFrame(getProvider().getFrameTime()); if (mAnimationCallbacks.size() > 0) { getProvider().postFrameCallback(this); } } };
getProvider方法得到的是MyFrameCallbackProvider,它的postFrameCallback如下:
private class MyFrameCallbackProvider implements AnimationFrameCallbackProvider { final Choreographer mChoreographer = Choreographer.getInstance(); @Override public void postFrameCallback(Choreographer.FrameCallback callback) { mChoreographer.postFrameCallback(callback); } @Override public void postCommitCallback(Runnable runnable) { mChoreographer.postCallback(Choreographer.CALLBACK_COMMIT, runnable, null); } @Override public long getFrameTime() { return mChoreographer.getFrameTime(); } @Override public long getFrameDelay() { return Choreographer.getFrameDelay(); } @Override public void setFrameDelay(long delay) { Choreographer.setFrameDelay(delay); } }
mChoreographer.postFrameCallback(callback)最终调用如下:
public void postFrameCallbackDelayed(FrameCallback callback, long delayMillis) { if (callback == null) { throw new IllegalArgumentException("callback must not be null"); } postCallbackDelayedInternal(CALLBACK_ANIMATION, callback, FRAME_CALLBACK_TOKEN, delayMillis); }
private void postCallbackDelayedInternal(int callbackType, Object action, Object token, long delayMillis) { if (DEBUG_FRAMES) { Log.d(TAG, "PostCallback: type=" + callbackType + ", action=" + action + ", token=" + token + ", delayMillis=" + 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); } } }
记住这里的callbackType是CALLBACK_ANIMATION,调用mCallbackQueues[callbackType].addCallbackLocked(dueTime, action, token)方法绑定CALLBACK_ANIMATION和action并放入mCallbackQueues中,然后若没有延迟时间则执行scheduleFrameLocked方法,传入的是当前时间:
private void scheduleFrameLocked(long now) { if (!mFrameScheduled) { mFrameScheduled = true; if (USE_VSYNC) { if (DEBUG_FRAMES) { 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 / TimeUtils.NANOS_PER_MS + sFrameDelay, now); if (DEBUG_FRAMES) { Log.d(TAG, "Scheduling next frame in " + (nextFrameTime - now) + " ms."); } Message msg = mHandler.obtainMessage(MSG_DO_FRAME); msg.setAsynchronous(true); mHandler.sendMessageAtTime(msg, nextFrameTime); } } }
这里不管USE_VSYNC是否为true,最后都会走到Choreographer的doFrame方法中:
void doFrame(long frameTimeNanos, int frame) { final long startNanos; synchronized (mLock) { if (!mFrameScheduled) { return; // no work to do } if (DEBUG_JANK && mDebugPrintNextFrameTimeDelta) { mDebugPrintNextFrameTimeDelta = false; Log.d(TAG, "Frame time delta: " + ((frameTimeNanos - mLastFrameTimeNanos) * 0.000001f) + " ms"); } long intendedFrameTimeNanos = frameTimeNanos; startNanos = System.nanoTime(); final long jitterNanos = startNanos - frameTimeNanos; if (jitterNanos >= mFrameIntervalNanos) { final long skippedFrames = jitterNanos / mFrameIntervalNanos; if (skippedFrames >= SKIPPED_FRAME_WARNING_LIMIT) { Log.i(TAG, "Skipped " + skippedFrames + " frames! " + "The application may be doing too much work on its main thread."); } final long lastFrameOffset = jitterNanos % mFrameIntervalNanos; if (DEBUG_JANK) { Log.d(TAG, "Missed vsync by " + (jitterNanos * 0.000001f) + " ms " + "which is more than the frame interval of " + (mFrameIntervalNanos * 0.000001f) + " ms! " + "Skipping " + skippedFrames + " frames and setting frame " + "time to " + (lastFrameOffset * 0.000001f) + " ms in the past."); } frameTimeNanos = startNanos - lastFrameOffset; } if (frameTimeNanos < mLastFrameTimeNanos) { if (DEBUG_JANK) { Log.d(TAG, "Frame time appears to be going backwards. May be due to a " + "previously skipped frame. Waiting for next vsync."); } scheduleVsyncLocked(); return; } if (mFPSDivisor > 1) { long timeSinceVsync = frameTimeNanos - mLastFrameTimeNanos; if (timeSinceVsync < (mFrameIntervalNanos * mFPSDivisor) && timeSinceVsync > 0) { scheduleVsyncLocked(); return; } } mFrameInfo.setVsync(intendedFrameTimeNanos, frameTimeNanos); mFrameScheduled = false; mLastFrameTimeNanos = frameTimeNanos; } try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, "Choreographer#doFrame"); AnimationUtils.lockAnimationClock(frameTimeNanos / TimeUtils.NANOS_PER_MS); mFrameInfo.markInputHandlingStart(); doCallbacks(Choreographer.CALLBACK_INPUT, frameTimeNanos); mFrameInfo.markAnimationsStart(); doCallbacks(Choreographer.CALLBACK_ANIMATION, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_INSETS_ANIMATION, frameTimeNanos); mFrameInfo.markPerformTraversalsStart(); doCallbacks(Choreographer.CALLBACK_TRAVERSAL, frameTimeNanos); doCallbacks(Choreographer.CALLBACK_COMMIT, frameTimeNanos); } finally { AnimationUtils.unlockAnimationClock(); Trace.traceEnd(Trace.TRACE_TAG_VIEW); } if (DEBUG_FRAMES) { final long endNanos = System.nanoTime(); Log.d(TAG, "Frame " + frame + ": Finished, took " + (endNanos - startNanos) * 0.000001f + " ms, latency " + (startNanos - frameTimeNanos) * 0.000001f + " ms."); } }
这里前面会检查纳秒级别的变化,如果符合更新要求则会执行一系列doCallbacks,doCallbacks中截取部分代码如下:
void doCallbacks(int callbackType, long frameTimeNanos) { CallbackRecord callbacks; synchronized (mLock) { // We use "now" to determine when callbacks become due because it's possible // for earlier processing phases in a frame to post callbacks that should run // in a following phase, such as an input event that causes an animation to start. final long now = System.nanoTime(); callbacks = mCallbackQueues[callbackType].extractDueCallbacksLocked( now / TimeUtils.NANOS_PER_MS); if (callbacks == null) { return; } } try { Trace.traceBegin(Trace.TRACE_TAG_VIEW, CALLBACK_TRACE_TITLES[callbackType]); for (CallbackRecord c = callbacks; c != null; c = c.next) { if (DEBUG_FRAMES) { Log.d(TAG, "RunCallback: type=" + callbackType + ", action=" + c.action + ", token=" + c.token + ", latencyMillis=" + (SystemClock.uptimeMillis() - c.dueTime)); } c.run(frameTimeNanos); } } finally { synchronized (mLock) { mCallbacksRunning = false; do { final CallbackRecord next = callbacks.next; recycleCallbackLocked(callbacks); callbacks = next; } while (callbacks != null); } Trace.traceEnd(Trace.TRACE_TAG_VIEW); } }
可以看到,这里从mCallbackQueues中根据callbackType找到action,执行它的run方法,找到CallbackQueue的addCallbackLocked方法:
public void addCallbackLocked(long dueTime, Object action, Object token) { CallbackRecord callback = obtainCallbackLocked(dueTime, action, token); ... ... }
可以看到,根据CALLBACK_ANIMATION找到的就是CallbackRecord:
private static final class CallbackRecord { public CallbackRecord next; public long dueTime; public Object action; // Runnable or FrameCallback public Object token; @UnsupportedAppUsage public void run(long frameTimeNanos) { if (token == FRAME_CALLBACK_TOKEN) { ((FrameCallback)action).doFrame(frameTimeNanos); } else { ((Runnable)action).run(); } } }
这里的action就是前面的mFrameCallback,它的doFrame方法中调用了doAnimationFrame方法:
private void doAnimationFrame(long frameTime) { long currentTime = SystemClock.uptimeMillis(); final int size = mAnimationCallbacks.size(); for (int i = 0; i < size; i++) { final AnimationFrameCallback callback = mAnimationCallbacks.get(i); if (callback == null) { continue; } if (isCallbackDue(callback, currentTime)) { callback.doAnimationFrame(frameTime); if (mCommitCallbacks.contains(callback)) { getProvider().postCommitCallback(new Runnable() { @Override public void run() { commitAnimationFrame(callback, getProvider().getFrameTime()); } }); } } } cleanUpList(); }
mAnimationCallbacks就是前面start时调用addAnimationFrameCallback传入的ValueAnimator,ValueAnimator继承了AnimationHandler.AnimationFrameCallback接口,所以这里会走到ValueAnimator的doAnimationFrame中:
public final boolean doAnimationFrame(long frameTime) { if (mStartTime < 0) { // First frame. If there is start delay, start delay count down will happen *after* this // frame. mStartTime = mReversing ? frameTime : frameTime + (long) (mStartDelay * resolveDurationScale()); } // Handle pause/resume if (mPaused) { mPauseTime = frameTime; removeAnimationCallback(); return false; } else if (mResumed) { mResumed = false; if (mPauseTime > 0) { // Offset by the duration that the animation was paused mStartTime += (frameTime - mPauseTime); } } if (!mRunning) { // If not running, that means the animation is in the start delay phase of a forward // running animation. In the case of reversing, we want to run start delay in the end. if (mStartTime > frameTime && mSeekFraction == -1) { // This is when no seek fraction is set during start delay. If developers change the // seek fraction during the delay, animation will start from the seeked position // right away. return false; } else { // If mRunning is not set by now, that means non-zero start delay, // no seeking, not reversing. At this point, start delay has passed. mRunning = true; startAnimation(); } } if (mLastFrameTime < 0) { if (mSeekFraction >= 0) { long seekTime = (long) (getScaledDuration() * mSeekFraction); mStartTime = frameTime - seekTime; mSeekFraction = -1; } mStartTimeCommitted = false; // allow start time to be compensated for jank } mLastFrameTime = frameTime; // The frame time might be before the start time during the first frame of // an animation. The "current time" must always be on or after the start // time to avoid animating frames at negative time intervals. In practice, this // is very rare and only happens when seeking backwards. final long currentTime = Math.max(frameTime, mStartTime); boolean finished = animateBasedOnTime(currentTime); if (finished) { endAnimation(); } return finished; }
这里调用了animateBasedOnTime方法,里面又调用了animateValue方法,到此,整个流程就串起来了。
每次执行animateBasedOnTime方法都会判断动画是否已经结束,如果结束了会走endAnimation方法,执行一些动画结束之后的回调。
-
关于argb颜色变化
前面我们看到ValueAnimator中有一个ofArgb方法,其中有一句anim.setEvaluator(ArgbEvaluator.getInstance()),这里传入了一个ArgbEvaluator实例,它的evaluate方法如下:
public Object evaluate(float fraction, Object startValue, Object endValue) { int startInt = (Integer) startValue; float startA = ((startInt >> 24) & 0xff) / 255.0f; float startR = ((startInt >> 16) & 0xff) / 255.0f; float startG = ((startInt >> 8) & 0xff) / 255.0f; float startB = ( startInt & 0xff) / 255.0f; int endInt = (Integer) endValue; float endA = ((endInt >> 24) & 0xff) / 255.0f; float endR = ((endInt >> 16) & 0xff) / 255.0f; float endG = ((endInt >> 8) & 0xff) / 255.0f; float endB = ( endInt & 0xff) / 255.0f; // convert from sRGB to linear startR = (float) Math.pow(startR, 2.2); startG = (float) Math.pow(startG, 2.2); startB = (float) Math.pow(startB, 2.2); endR = (float) Math.pow(endR, 2.2); endG = (float) Math.pow(endG, 2.2); endB = (float) Math.pow(endB, 2.2); // compute the interpolated color in linear space float a = startA + fraction * (endA - startA); float r = startR + fraction * (endR - startR); float g = startG + fraction * (endG - startG); float b = startB + fraction * (endB - startB); // convert back to sRGB in the [0..255] range a = a * 255.0f; r = (float) Math.pow(r, 1.0 / 2.2) * 255.0f; g = (float) Math.pow(g, 1.0 / 2.2) * 255.0f; b = (float) Math.pow(b, 1.0 / 2.2) * 255.0f; return Math.round(a) << 24 | Math.round(r) << 16 | Math.round(g) << 8 | Math.round(b); }
这里解析一下这里的算法,startValue和endValue都是32位的Integer类型,像这样:ValueAnimator.ofArgb(0xFFF00000,0xFFFFFF00)。从左至右A(alpha)、R(red)、G(green)、B(blue)色道各占8位,分别取出后通过和255的占比比率依次计算后再加到一起组成新的色值。