I am using an ObjectAnimator to make a long text on a TextView to be scrolled inside a HorizontalScrollView. The scroll animation is working nicely, the text is being scrolled correctly, but I am having trouble to make the animation stop for a few moments before it restarts.
I am using the following classes to make such animations:
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);
}
}
Those classes are utilized in a RecyclerView.Adapter (cause those scrolling texts are located inside the items of a RecyclerView) and are utilized as follows:
@Override
public void onBindViewHolder(@NonNull ViewHolderInterface.MainListViewHolder holder, int position) {
\\------------
\\omitting code that does not relate to animations
\\------------
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);
}
where beforeDraw is just a method created to allow me to find the views' proper size on the screen to decide if the text should be scrolled or not.
Anyway, the problem I am facing is the fact that, when utilizing the onAnimationRepeat(Animator animation), the animation restarts, and just then it pauses for the time determined as delay to restart scrolling. This delay before resuming the animation is part of what I envisioned, but it isn't everything that I want to achieve, since what I wanted is that the animation also paused after its completion but before its restart.
To be more clear, my desired outcome would be as follows:
Delay -> Start Animation -> Complete Scroll Animation -> Pause at the End -> Restart Animation with Delay -> ...
However, what I am getting so far would be like this:
Delay -> Start Animation -> Complete Scroll Animation -> **No Pause** -> Restart Animation with Delay -> ...
Is it possible to achieve such thing with ObjectAnimator? I changed from Animation to ObjectAnimator precisely because I wanted to pause the animation before restarting it.
Also, I would prefer to have a solution that uses the setRepeatCount(ValueAnimator.INFINITE) option since I want to make sure it will never stop scrolling even if the user does forget his phone unlocked.
Thanks in advance for all the help!
updateListenercheck with some math on the interpolation values; but I did accomplish what it seems you are after by simply removing the repeat functionality (it won't support what you want for reasons of implementation in the ValueAnimator) and addnew Handler().postDelayed(animation::start, delayMillis);to theonAnimationEndinDelayAnimation. This pauses the animation at its end before restarting it. If the start delay and the "repeat" delay need to be managed separately then you can clear the start delay on the first "end".