3.4 Reverse 流程
java
public void reverse() {
start(true, true); // inReverse=true, selfPulse=true
}
条件 :canReverse() 要求 getTotalDuration() != DURATION_INFINITE(无限时长的 AnimatorSet 不可反向)。
reverse 的本质 :不是在运行中翻转方向,而是以反向模式重新 start()。mReversing = true 影响以下行为:
| 行为 | 正向 | 反向 |
|---|---|---|
handleAnimationEvents 遍历方向 |
从 mLastEventId+1 向前 | 从 mLastEventId-1 向后 |
| 事件语义 | START→启动, END→结束 | END→reverse启动, DELAY_ENDED→结束 |
findLatestEventIdForTime |
找 time <= currentPlayTime |
找 time >= (totalDuration - currentPlayTime) |
getPlayTimeForNodeIncludingDelay |
overallPlayTime - node.mStartTime |
node.mEndTime - (totalDuration - overallPlayTime) |
| 结束条件 | mPlayingSet 空且 mLastEventId 到末尾 | mPlayingSet 仅剩 Root 或 mLastEventId < 3 |
3.5 Seek 流程
java
public void setCurrentPlayTime(long playTime) {
initAnimation(); // 确保依赖图已构建
long lastPlayTime = mSeekState.getPlayTime();
if (!isStarted() || isPaused()) {
if (!mSeekState.isActive()) {
// 首次 seek:初始化子动画起始值
findLatestEventIdForTime(0);
initChildren();
skipToEndValue(!mReversing);
mSeekState.setPlayTime(0, mReversing);
}
}
mSeekState.setPlayTime(playTime, mReversing);
animateBasedOnPlayTime(playTime, lastPlayTime, mReversing);
}
SeekState 的作用 :存储 seek 位置和方向,在 doAnimationFrame 首帧时应用:
java
if (mSeekState.isActive()) {
mSeekState.updateSeekDirection(mReversing);
mFirstFrame = frameTime - mSeekState.getPlayTime() * durationScale;
mSeekState.reset();
}
animateBasedOnPlayTime 通过 mChildStartAndStopTimes 二分查找定位区间,逐个时间点调用 animateSkipToEnds(跳到端点)和 animateValuesInRange(计算中间值)。
3.6 Start 流程
java
start(false, true):
mStarted = true
mSelfPulse = true
重置所有 Node.mEnded = false
initAnimation() ← 构建 DAG + events
mReversing = false
if (!isEmptySet):
startAnimation():
1. addAnimationEndListener() ← 为子动画添加结束监听
2. addAnimationCallback(0) ← 注册到 AnimationHandler
3. if mShouldResetValuesAtStart:
initChildren() + skipToEndValue(!mReversing) ← 重置到起始值
4. if mReversing || mStartDelay == 0 || mSeekState.isActive():
快进到当前播放位置:
playTime = seek ? mSeekState.getPlayTime() : 0
toId = findLatestEventIdForTime(playTime)
handleAnimationEvents(-1, toId, playTime) ← 从头处理到当前位置
清理已结束的 mPlayingSet
mLastEventId = toId
notifyStartListeners(inReverse)
isEmptySet 检查:如果 AnimatorSet 不含任何非 AnimatorSet 子动画且无 startDelay,直接 start + end。
快进条件 :仅在反向、无延迟、或有 seek 时立即快进。正向 + 有 startDelay 时等待 doAnimationFrame 中 delay 过去后自然启动。
3.7 doAnimationFrame 流程
java
doAnimationFrame(frameTime):
if durationScale == 0: forceToEnd(); return true
if mFirstFrame < 0: mFirstFrame = frameTime ← 记录首帧
// pause/resume 处理
if mPaused: mPauseTime = frameTime; removeCallback; return false
if mPauseTime > 0: mFirstFrame += (frameTime - mPauseTime); mPauseTime = -1
// seek 对齐
if mSeekState.isActive():
mFirstFrame = frameTime - mSeekState.getPlayTime() * durationScale
mSeekState.reset()
// startDelay 等待
if !mReversing && frameTime < mFirstFrame + mStartDelay * durationScale:
return false
// 计算播放时间
unscaledPlayTime = (frameTime - mFirstFrame) / durationScale
mLastFrameTime = frameTime
// 1. 处理事件(启动/结束子动画)
latestId = findLatestEventIdForTime(unscaledPlayTime)
handleAnimationEvents(mLastEventId, latestId, unscaledPlayTime)
mLastEventId = latestId
// 2. 向活跃子动画注入帧
for node in mPlayingSet:
if !node.mEnded:
pulseFrame(node, getPlayTimeForNodeIncludingDelay(unscaledPlayTime, node))
// 3. 清理已结束的子动画
mPlayingSet.removeIf(n -> n.mEnded)
// 4. 判断是否全部完成
finished = mPlayingSet.isEmpty() && mLastEventId == mEvents.size() - 1
if finished: endAnimation(); return true
return false
handleAnimationEvents 正向时从 mLastEventId+1 到 latestId 遍历:
ANIMATION_START:mPlayingSet.add(node)+node.mAnimation.startWithoutPulsing(false)+pulseFrame(node, 0)ANIMATION_END:pulseFrame(node, playTime)(注入最后一帧)
pulseFrame :node.mAnimation.pulseAnimationFrame(animPlayTime * durationScale),返回 true 表示子动画已结束,设置 node.mEnded = true。
3.8 Pause 流程
java
pause():
super.pause() ← mPaused = true
mPauseTime = -1 ← 标记,等待下一帧记录暂停时刻
callOnPlayingSet(Animator::pause) ← 暂停所有活跃子动画
下一帧 doAnimationFrame 中:
java
if mPaused:
mPauseTime = frameTime ← 记录暂停时刻
removeAnimationCallback() ← 注销帧回调,停止接收帧
return false
子动画的 pause 由 AnimatorSet 委托调用,但子动画不自行注册帧回调,所以实际暂停效果由 AnimatorSet 的 removeAnimationCallback 实现。
3.9 Resume 流程
java
resume():
super.resume() ← mPaused = false
if mPauseTime >= 0:
addAnimationCallback(0) ← 重新注册帧回调
callOnPlayingSet(Animator::resume) ← 恢复所有子动画
下一帧 doAnimationFrame 中:
java
if mPauseTime > 0:
mFirstFrame += (frameTime - mPauseTime) ← 偏移首帧,跳过暂停期间
mPauseTime = -1
3.10 End 流程
java
end():
if mShouldIgnoreEndWithoutStart && !isStarted(): return ← Pre-N 兼容
if isStarted():
mStarted = false ← 防止重入
if mReversing:
// 反向:从 mLastEventId-1 向后遍历
for eventId = mLastEventId-1 downto 0:
if ANIMATION_END: anim.reverse() ← 反向启动
if ANIMATION_DELAY_ENDED && anim.isStarted(): anim.end() ← 结束
else:
// 正向:从 mLastEventId+1 向前遍历
for eventId = mLastEventId+1 to size:
if ANIMATION_START: anim.start() ← 启动未启动的
if ANIMATION_END && anim.isStarted(): anim.end() ← 结束未结束的
endAnimation() ← 清理状态
end 的语义:快速跳过所有未处理的事件,将所有子动画推到最终状态。正向时启动所有未启动的、结束所有未结束的;反向时反转所有 END 事件、结束所有 DELAY_ENDED 事件。
3.11 Stop 流程
AnimatorSet 没有 stop() 方法。结束动画通过 end() 或 cancel() 实现。内部 endAnimation() 是状态清理的统一入口:
java
private void endAnimation() {
mStarted = false
mLastFrameTime = -1
mFirstFrame = -1
mLastEventId = -1
mPaused = false
mPauseTime = -1
mSeekState.reset()
mPlayingSet.clear()
removeAnimationCallback() ← 注销帧回调
notifyEndListeners(mReversing) ← 通知 onAnimationEnd
removeAnimationEndListener() ← 移除子动画监听
mSelfPulse = true
mReversing = false
}
3.12 Cancel 流程
java
cancel():
if isStarted() || mStartListenersCalled:
notifyListeners(ON_CANCEL) ← 通知 onAnimationCancel
callOnPlayingSet(Animator::cancel) ← 取消所有活跃子动画
mPlayingSet.clear()
endAnimation() ← 清理状态
cancel 与 end 的区别:
cancel:子动画在当前位置停止,触发onAnimationCancel,然后endAnimationend:子动画被推到最终状态(start/end),不触发 cancel
callOnPlayingSet 对 mPlayingSet 中每个子动画调用 cancel(),子动画的 cancel() 会触发自身的 onAnimationCancel 和 onAnimationEnd。