【阅读源码--Android】动画之ValueAnimator--2

📅 发布时间:2026/8/9 4:55:18
【阅读源码--Android】动画之ValueAnimator--2 5. 值计算管线详解一帧调用栈本节围绕一次 vsync 帧到达时从AnimationHandler到最终计算出动画值的完整调用栈结合前述状态字段和时间变量逐函数解析。5.1 调用栈总览AnimationHandler.doAnimationFrame(frameTime) ← 遍历所有活跃 Animator │ └─ ValueAnimator.doAnimationFrame(frameTime) ← [1] 帧入口时间与状态处理 │ ├─ startAnimation() ← [2] 首帧启动条件触发 │ └─ animateBasedOnTime(currentTime) ← [3] 时间→总 fraction │ ├─ notifyListeners(ON_REPEAT) ← [4] 重复通知条件触发 │ └─ getCurrentIterationFraction(fraction) ← [5] 总 fraction→迭代 fraction │ ├─ getCurrentIteration(fraction) ← [6] 拆分迭代号 │ └─ shouldPlayBackward(iteration) ← [7] 判断方向 │ └─ animateValue(iterFraction) ← [8] 核心值计算 │ ├─ mInterpolator.getInterpolation()← [9] 节奏变换 │ ├─ mValues[i].calculateValue() ← [10] 类型求值 │ │ │ └─ mKeyframes.getValue() ← [11] 关键帧插值 │ │ │ └─ mEvaluator.evaluate() ← [12] Evaluator 线性插值 │ └─ 通知 mUpdateListeners ← [13] 回调5.2 [1] doAnimationFrame(frameTime) — 帧入口这是AnimationHandler遍历mAnimationCallbacks时对每个 Animator 调用的入口。负责处理所有与时间、状态相关的前置逻辑。执行流程doAnimationFrame(frameTime):步骤 1初始化 mStartTime首帧if(mStartTime0){// 首帧mStartTimemReversing?frameTime// 反向播放startDelay 放在末尾:frameTime(long)(mStartDelay*resolveDurationScale());// 正向startDelay 加到起始}mStartTime 0初始值-1表示首次收到帧回调正向播放时mStartTime 帧时间 缩放后的 startDelay在此时间之前动画不计算值反向播放时mStartTime 帧时间startDelay 逻辑在动画末尾处理步骤 2处理 pause/resumeif(mPaused){mPauseTimeframeTime;// 记录暂停时刻removeAnimationCallback();// 从 AnimationHandler 注销不再收帧returnfalse;}elseif(mResumed){mResumedfalse;if(mPauseTime0){mStartTime(frameTime-mPauseTime);// 偏移 startTime跳过暂停期间}}暂停时记录mPauseTime并注销回调动画冻结恢复时将mStartTime向后偏移(frameTime - mPauseTime)等效于暂停期间时间不流逝步骤 3处理 startDelay 等待期if(!mRunning){if(mStartTimeframeTimemSeekFraction-1){returnfalse;// 还在 startDelay 中跳过本帧}else{mRunningtrue;startAnimation();// startDelay 结束正式开始}}mRunning false且mStartTime frameTime仍在延迟等待中直接返回延迟结束后设置mRunning true调用startAnimation()通知监听器步骤 4处理 Seek首帧if(mLastFrameTime0){// 首帧与上面的首帧不同这是进入帧循环的首帧if(mSeekFraction0){longseekTime(long)(getScaledDuration()*mSeekFraction);mStartTimeframeTime-seekTime;// 将 startTime 前移使 fraction 对齐到 seek 位置mSeekFraction-1;}mStartTimeCommittedfalse;// 允许后续 jank 补偿}mLastFrameTimeframeTime;步骤 5计算值并判断结束finallongcurrentTimeMath.max(frameTime,mStartTime);// 防止负时间seek 回退时booleanfinishedanimateBasedOnTime(currentTime);if(finished){endAnimation();}returnfinished;5.3 [2] startAnimation() — 首帧启动仅在 startDelay 结束、动画正式开始时调用一次。privatevoidstartAnimation(){mAnimationEndRequestedfalse;initAnimation();// 初始化 PropertyValuesHolder分配 EvaluatormRunningtrue;mOverallFraction(mSeekFraction0)?mSeekFraction:0f;notifyStartListeners(mReversing);// 通知 onAnimationStart}initAnimation()调用PropertyValuesHolder.init()为每个mValues[i]分配默认 EvaluatorInt/Float并传递给 KeyframeSetmOverallFraction初始化为 seek 位置或 05.4 [3] animateBasedOnTime(currentTime) — 时间→总 fraction这是从时间到 fraction 的核心转换同时处理重复检测和结束判断。booleananimateBasedOnTime(longcurrentTime){booleandonefalse;if(mRunning){finallongscaledDurationgetScaledDuration();// ── 时间 → 总 fraction ──finalfloatfractionscaledDuration0?(float)(currentTime-mStartTime)/scaledDuration:1f;// 0 时长直接跳到结束finalfloatlastFractionmOverallFraction;// ── 重复检测 ──finalbooleannewIteration(int)fraction(int)lastFraction;finalbooleanlastIterationFinished(fractionmRepeatCount1)(mRepeatCount!INFINITE);if(scaledDuration0){donetrue;// 0 时长直接结束}elseif(newIteration!lastIterationFinished){notifyListeners(AnimatorCaller.ON_REPEAT,false);// 进入新迭代}elseif(lastIterationFinished){donetrue;// 所有迭代完成}mOverallFractionclampFraction(fraction);// ── 总 fraction → 迭代 fraction → 值 ──floatcurrentIterationFractiongetCurrentIterationFraction(mOverallFraction,mReversing);animateValue(currentIterationFraction);}returndone;}关键变量说明变量含义示例fraction总进度 已过时间 / 缩放时长2.3 第 2 次重复进度 30%lastFraction上一帧的总进度用于检测迭代切换0.9 → 1.1 说明进入了新迭代mOverallFraction当前保存的总进度clamp 后clamp 到[0, mRepeatCount1]newIteration(int)fraction (int)lastFraction0.9→1.10 1 truelastIterationFinishedfraction mRepeatCount 1repeatCount2fraction≥3 结束5.5 [5][6][7] getCurrentIterationFraction — 总 fraction → 迭代 fraction这三个函数协作将总 fraction可能 1转换为[0,1]的当前迭代 fraction同时处理 REVERSE 模式的方向交替。getCurrentIterationFraction(fraction, inReverse)privatefloatgetCurrentIterationFraction(floatfraction,booleaninReverse){fractionclampFraction(fraction);// 限制到 [0, mRepeatCount1]intiterationgetCurrentIteration(fraction);// 拆分迭代号floatcurrentFractionfraction-iteration;// 迭代内进度 ∈ [0,1]returnshouldPlayBackward(iteration,inReverse)?1f-currentFraction// 反向1→0:currentFraction;// 正向0→1}getCurrentIteration(fraction)privateintgetCurrentIteration(floatfraction){doubleiterationMath.floor(fraction);// 特殊处理fraction 恰好为整数时视为上一个迭代的结束if(fractioniterationfraction0){iteration--;}return(int)iteration;}示例fraction 2.0→iteration 1currentFraction 1.0第 1 次迭代的末尾而非第 2 次的开头。shouldPlayBackward(iteration, inReverse)privatebooleanshouldPlayBackward(intiteration,booleaninReverse){if(iteration0mRepeatModeREVERSE(iteration(mRepeatCount1)||mRepeatCountINFINITE)){if(inReverse){return(iteration%2)0;// 反向启动时偶数迭代反向}else{return(iteration%2)!0;// 正向启动时奇数迭代反向}}else{returninReverse;// 非逆序模式或第 0 次迭代}}5.6 Repeat 与 Reverse 原理Repeat 机制Repeat 通过总 fraction 超过 1来驱动。mOverallFraction可以大于 1整数部分代表已完成的迭代数小数部分代表当前迭代进度。mOverallFraction 2.35 │ ├─ iteration 2 已完成 2 次迭代正在第 3 次 └─ iterFraction 0.35 当前迭代进度 35%mRepeatCount 0不重复fractionclamp 到[0, 1]mRepeatCount 3fractionclamp 到[0, 4]共播放 4 次mRepeatCount INFINITE不设上界RESTART 模式每次迭代都从 0→1shouldPlayBackward始终返回inReverse正向时为 false。REVERSE 模式奇偶迭代方向交替iterationshouldPlayBackward正向启动实际方向fraction 变化0false正向0 → 11true反向1 → 02false正向0 → 13true反向1 → 0Reverse 机制reverse()方法翻转动画方向通过修改mReversing标志和mStartTime实现publicvoidreverse(){if(isPulsingInternal()){// 运行中计算剩余时间将 startTime 对齐到从另一端开始longcurrentTimeAnimationUtils.currentAnimationTimeMillis();longcurrentPlayTimecurrentTime-mStartTime;longtimeLeftgetScaledDuration()-currentPlayTime;mStartTimecurrentTime-timeLeft;// 使 fraction 翻转mStartTimeCommittedtrue;mReversing!mReversing;}elseif(mStarted){mReversing!mReversing;end();// 已 start 但未运行直接结束}else{start(true);// 未 start以反向模式启动}}mReversing传入getCurrentIterationFraction的inReverse参数影响shouldPlayBackward的计算从而决定每个迭代的播放方向。reverse REVERSE repeatMode 的组合mReversing true时shouldPlayBackward的奇偶判断取反导致迭代的实际方向与mReversing false时完全相反。5.7 [8] animateValue(fraction) — 核心值计算voidanimateValue(floatfraction){// [9] 节奏变换fractionmInterpolator.getInterpolation(fraction);mCurrentFractionfraction;// 保存插值后 fraction供 getAnimatedFraction() 查询// [10] 类型求值对每个 PropertyValuesHolder 计算值for(inti0;inumValues;i){mValues[i].calculateValue(fraction);}// [13] 通知 UpdateListenerif(mSeekFraction0||mStartListenersCalled){callOnList(mUpdateListeners,AnimatorCaller.ON_UPDATE,this,false);}}注意传入animateValue的 fraction 已经是[0,1]范围的迭代 fraction。mInterpolator在此对其进行非线性映射输出可能超出[0,1]如过冲、弹跳。5.8 [10][11][12] calculateValue → getValue → evaluate — 值计算链PropertyValuesHolder.calculateValue(fraction) │ └─ mKeyframes.getValue(fraction) ← KeyframeSet 查找区间并插值 │ ├─ 定位区间 [prevKeyframe, nextKeyframe] │ ├─ 计算 intervalFraction (fraction - prevFraction) / (nextFraction - prevFraction) │ ├─ 应用局部 Interpolator: intervalFraction nextKf.getInterpolator().getInterpolation(intervalFraction) │ 若该 Keyframe 未设置 Interpolator则使用线性 │ └─ mEvaluator.evaluate(intervalFraction, prevValue, nextValue) │ └─ result startValue fraction * (endValue - startValue) ← 线性插值IntKeyframeSet / FloatKeyframeSet 的优化重写getIntValue()/getFloatValue()直接使用原始类型计算避免装箱开销。当mEvaluator null时使用内置线性公式否则调用 Evaluator。最终计算结果存入PropertyValuesHolder.mAnimatedValueObjectAnimator通过setAnimatedValue(target)将值设置到目标对象。6. Seek 机制通过setCurrentPlayTime()/setCurrentFraction()跳转到指定位置动画运行中直接修改mStartTime currentTime - seekTime未运行时设置mSeekFraction在首帧时应用doAnimationFrame第1556-1559行7. Jank 补偿首帧可能因调度延迟而产生 jank首帧设置mStartTimeCommitted false第1561行commitAnimationFrame(frameTime)被调用时计算adjustment frameTime - mLastFrameTime若adjustment 0修正mStartTime adjustment第1363行设置mStartTimeCommitted true后续不再补偿8. 事件通知8.1 生命周期监听器继承自Animator事件时机onAnimationStartstartAnimation()调用时考虑 startDelayonAnimationEndendAnimation()调用时onAnimationCancelcancel()调用时onAnimationRepeat新迭代开始时8.2 帧更新监听器publicinterfaceAnimatorUpdateListener{voidonAnimationUpdate(ValueAnimatoranimation);}在animateValue()中每次值计算完成后调用第1650-1652行。9. AnimatorSet 集成ValueAnimator提供了供AnimatorSet驱动的内部方法方法用途animateValuesInRange()在指定时间范围内计算动画值支持跨迭代animateSkipToEnds()跳过到起始/结束值pulseAnimationFrame()由父 AnimatorSet 驱动帧mSelfPulse false时startWithoutPulsing()启动但不自行注册帧回调10. 线程模型必须在拥有Looper的线程调用start()/cancel()/end()/resume()所有帧回调在调用start()的线程执行currentAnimationTimeMillis()基于ThreadLocal每个线程独立维护动画时钟11. 工厂方法方法值类型默认 EvaluatorofInt(int...)intIntEvaluatorofArgb(int...)int (color)ArgbEvaluatorofFloat(float...)floatFloatEvaluatorofObject(TypeEvaluator, Object...)任意对象自定义ofPropertyValuesHolder(PropertyValuesHolder...)多属性并行各自指定12. Clone 机制clone()执行深拷贝复制mUpdateListeners列表重置所有运行时状态时间、fraction、标志位深拷贝PropertyValuesHolder[]及其mValuesMap