Animator恢复后startDelay被忽略问题求助
解决Animator暂停时同步暂停startDelay的问题
这个问题确实挺棘手的,因为Android原生Animator的startDelay机制本身就是独立于动画暂停状态的——一旦启动,延迟计时器就会自顾自地走,不管你有没有暂停动画。不过我们可以通过自定义包装逻辑来解决这个问题,下面是几个可行的方案:
方案一:自定义可暂停的Animator包装类
我们可以创建一个包装类,手动管理动画的延迟时间和暂停状态,核心思路是在暂停时记录剩余的延迟时间,恢复时重新设置这个剩余延迟再启动动画。
代码示例:
public class PausableAnimator { private final Animator mAnimator; private long mOriginalStartDelay; private long mRemainingDelay; private long mPauseTime; private boolean mIsPaused = false; private boolean mHasStartedDelay = false; public PausableAnimator(Animator animator) { mAnimator = animator; mOriginalStartDelay = animator.getStartDelay(); mRemainingDelay = mOriginalStartDelay; // 监听动画开始,标记延迟阶段结束 mAnimator.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mHasStartedDelay = true; mRemainingDelay = 0; } @Override public void onAnimationEnd(Animator animation) { resetState(); } @Override public void onAnimationCancel(Animator animation) { resetState(); } @Override public void onAnimationRepeat(Animator animation) {} }); } public void start() { mAnimator.setStartDelay(mRemainingDelay); mAnimator.start(); } public void pause() { if (mIsPaused || !mAnimator.isRunning()) return; if (!mHasStartedDelay) { // 动画还在延迟阶段,计算剩余延迟 long elapsedTime = System.currentTimeMillis() - (mAnimator.getStartDelay() - mRemainingDelay); mRemainingDelay = Math.max(0, mOriginalStartDelay - elapsedTime); } else { // 动画已经开始执行,暂停原生动画 if (mAnimator instanceof ValueAnimator) { ((ValueAnimator) mAnimator).pause(); } else if (mAnimator instanceof AnimatorSet) { ((AnimatorSet) mAnimator).pause(); } } mPauseTime = System.currentTimeMillis(); mIsPaused = true; mAnimator.cancel(); // 先取消当前动画,避免延迟继续计时 } public void resume() { if (!mIsPaused) return; mIsPaused = false; if (!mHasStartedDelay) { // 恢复延迟阶段的动画,直接启动剩余延迟 start(); } else { // 恢复已经开始执行的动画 if (mAnimator instanceof ValueAnimator) { ((ValueAnimator) mAnimator).resume(); } else if (mAnimator instanceof AnimatorSet) { ((AnimatorSet) mAnimator).resume(); } else { // 对于低版本兼容,手动计算进度恢复 long elapsedPauseTime = System.currentTimeMillis() - mPauseTime; mAnimator.setCurrentPlayTime(mAnimator.getCurrentPlayTime() + elapsedPauseTime); mAnimator.start(); } } } private void resetState() { mIsPaused = false; mHasStartedDelay = false; mRemainingDelay = mOriginalStartDelay; mPauseTime = 0; } // 转发其他需要的Animator方法,比如更新延迟、添加监听等 public void setStartDelay(long delay) { mOriginalStartDelay = delay; mRemainingDelay = delay; if (!mIsPaused) { mAnimator.setStartDelay(delay); } } }
使用方式:
// 创建原动画 ObjectAnimator animator = ObjectAnimator.ofFloat(view, "alpha", 0f, 1f); animator.setStartDelay(2000); // 包装成可暂停动画 PausableAnimator pausableAnimator = new PausableAnimator(animator); // 启动、暂停、恢复 pausableAnimator.start(); pausableAnimator.pause(); // 此时延迟会暂停计时 pausableAnimator.resume(); // 恢复后会继续走完剩余的延迟再执行动画
方案二:针对AnimatorSet的递归处理
如果你的动画逻辑用到了AnimatorSet,需要递归处理它的所有子动画,确保每个子动画的延迟都能在暂停时同步暂停。可以在上面的包装类基础上扩展:
核心逻辑扩展:
private List<PausableAnimator> mChildPausables = new ArrayList<>(); public PausableAnimator(AnimatorSet animatorSet) { mAnimator = animatorSet; mOriginalStartDelay = animatorSet.getStartDelay(); mRemainingDelay = mOriginalStartDelay; // 包装所有子动画 for (Animator child : animatorSet.getChildAnimations()) { mChildPausables.add(new PausableAnimator(child)); } // 监听AnimatorSet的状态,同步子动画的暂停/恢复 animatorSet.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) { mHasStartedDelay = true; mRemainingDelay = 0; } @Override public void onAnimationEnd(Animator animation) { resetState(); for (PausableAnimator child : mChildPausables) { child.resetState(); } } @Override public void onAnimationCancel(Animator animation) { resetState(); for (PausableAnimator child : mChildPausables) { child.resetState(); } } @Override public void onAnimationRepeat(Animator animation) {} }); } @Override public void pause() { super.pause(); // 暂停所有子动画 for (PausableAnimator child : mChildPausables) { child.pause(); } } @Override public void resume() { super.resume(); // 恢复所有子动画 for (PausableAnimator child : mChildPausables) { child.resume(); } }
关键注意事项
- 如果你用到了动画的
setRepeatCount或者循环逻辑,需要在包装类中同步处理这些状态,避免恢复时出现异常。 - 对于API 19以下的版本,
ValueAnimator没有原生的pause()方法,需要手动保存当前动画进度,恢复时从该进度继续执行。 - 记得在动画结束或取消时重置包装类的状态,避免下次启动时出现延迟计算错误。
内容的提问来源于stack exchange,提问作者Katazo
相关产品推荐
相关产品推荐

