如何实现ObjectAnimator完成滚动动画后暂停再重启?
使用ObjectAnimator实现HorizontalScrollView内TextView长文本滚动,动画运行正常,但无法实现动画完成后暂停片刻再重启的需求。当前代码设置了setRepeatCount(ValueAnimator.INFINITE),但实际流程是动画结束后直接重启,没有预期的暂停环节。
预期流程:
Delay -> Start Animation -> Complete Scroll Animation -> Pause at the End -> Restart Animation with Delay -> ...
实际流程:
Delay -> Start Animation -> Complete Scroll Animation -> No Pause -> Restart Animation with Delay -> ...
原实现代码
class ObjectScroller { ObjectAnimator animator; TextView scrollText; long duration = 7500; long delay = 2500; public ObjectScroller(TextView scroll, int itemWidth, int viewWidth) { this.scrollText = scroll; animator = ObjectAnimator.ofFloat(scrollText, "translationX", viewWidth-(itemWidth + 50)); animator.setStartDelay(delay); animator.setInterpolator(new LinearInterpolator()); animator.setDuration(duration); animator.setRepeatMode(ValueAnimator.RESTART); animator.setRepeatCount(ValueAnimator.INFINITE); } public void startObjectScroll() { animator.addListener(new DelayAnimation(delay)); animator.start(); } } class DelayAnimation implements Animator.AnimatorListener { private long delayMillis; public DelayAnimation(long delayMillis) { this.delayMillis = delayMillis; } @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) {} @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) { animation.pause(); new Handler().postDelayed(animation::resume, delayMillis); } }
Adapter中的使用代码
@Override public void onBindViewHolder(@NonNull ViewHolderInterface.MainListViewHolder holder, int position) { // 省略无关代码 this.beforeDraw(holder.textInfoOne, () -> { Paint textPaint = holder.textInfoOne.getPaint(); String text = holder.textInfoOne.getText().toString(); int itemWidth = Math.round(textPaint.measureText(text)); int viewWidth = holder.scrollText.getWidth(); if (itemWidth > viewWidth) { holder.obScroller = new ObjectScroller(holder.textInfoOne, itemWidth,viewWidth); holder.obScroller.startObjectScroll(); } }); } public void beforeDraw(View view, Runnable runnable) { ViewTreeObserver.OnPreDrawListener preDraw = new ViewTreeObserver.OnPreDrawListener() { @Override public boolean onPreDraw() { view.getViewTreeObserver().removeOnPreDrawListener(this); runnable.run(); return true; } }; view.getViewTreeObserver().addOnPreDrawListener(preDraw); }
问题原因
原方案依赖系统的setRepeatCount(INFINITE)和onAnimationRepeat回调,但onAnimationRepeat的触发时机是动画即将开始重复的时候,此时系统已经完成了动画的重置准备,调用pause()和resume()无法实现“动画结束后暂停”的效果,只会让重复后的动画延迟启动,而非结束后暂停。
解决方案
放弃系统的自动重复机制,改为在动画结束后手动延迟重启,同时处理RecyclerView复用场景的动画清理:
- 修改
ObjectScroller类,移除系统重复配置,在onAnimationEnd中实现延迟重启 - 在RecyclerView的
onViewRecycled中停止动画,避免复用ViewHolder时出现动画混乱
调整后的ObjectScroller代码
import android.os.Handler; import android.os.Looper; import android.view.animation.LinearInterpolator; import android.widget.TextView; import androidx.animation.ObjectAnimator; import androidx.animation.Animator; class ObjectScroller { private TextView scrollText; private final long duration = 7500; private final long delay = 2500; private final float targetTranslationX; private Handler handler; public ObjectScroller(TextView scroll, int itemWidth, int viewWidth) { this.scrollText = scroll; this.targetTranslationX = viewWidth - (itemWidth + 50); this.handler = new Handler(Looper.getMainLooper()); } private ObjectAnimator createAnimator() { ObjectAnimator anim = ObjectAnimator.ofFloat(scrollText, "translationX", 0f, targetTranslationX); anim.setInterpolator(new LinearInterpolator()); anim.setDuration(duration); anim.addListener(new Animator.AnimatorListener() { @Override public void onAnimationStart(Animator animation) {} @Override public void onAnimationEnd(Animator animation) { // 动画结束后,延迟指定时间再重启 handler.postDelayed(() -> { // 先重置translationX到初始位置 scrollText.setTranslationX(0f); createAnimator().start(); }, delay); } @Override public void onAnimationCancel(Animator animation) {} @Override public void onAnimationRepeat(Animator animation) {} }); return anim; } public void startObjectScroll() { // 第一次启动添加初始延迟 handler.postDelayed(() -> createAnimator().start(), delay); } public void stopScroll() { if (handler != null) { handler.removeCallbacksAndMessages(null); } // 重置位置 scrollText.setTranslationX(0f); } }
修改后的Adapter代码
@Override public void onBindViewHolder(@NonNull ViewHolderInterface.MainListViewHolder holder, int position) { // 先停止之前的动画(如果有) if (holder.obScroller != null) { holder.obScroller.stopScroll(); holder.obScroller = null; } // 省略无关代码 this.beforeDraw(holder.textInfoOne, () -> { Paint textPaint = holder.textInfoOne.getPaint(); String text = holder.textInfoOne.getText().toString(); int itemWidth = Math.round(textPaint.measureText(text)); int viewWidth = holder.scrollText.getWidth(); if (itemWidth > viewWidth) { holder.obScroller = new ObjectScroller(holder.textInfoOne, itemWidth, viewWidth); holder.obScroller.startObjectScroll(); } }); } // 添加ViewHolder回收时的动画清理 @Override public void onViewRecycled(@NonNull ViewHolderInterface.MainListViewHolder holder) { super.onViewRecycled(holder); if (holder.obScroller != null) { holder.obScroller.stopScroll(); holder.obScroller = null; } }
方案说明
这个方案完全通过手动控制动画的结束与重启,实现了“动画完成后暂停再循环”的需求,同时保留了无限滚动的效果。另外添加了stopScroll方法和RecyclerView的回收处理,避免了ViewHolder复用导致的动画冲突和内存泄漏问题。
内容的提问来源于stack exchange,提问作者CorMatt

