I'm building a change animation for my RecyclerView. I've overriden canReuseUpdatedViewHolder()
to return true
in order to retain the previous viewholder. I start the animation in animateChange()
and call dispatchAnimationFinished()
as soon as it ends. Params oldHolder
and newHolder
are the same instance in animateChange()
.
Then, as soon as the animation starts, RecyclerView's onBindViewHolder()
is called for every child in the list. Surprisingly, only for the animated item a new ViewHolder is spawned in onCreateViewHolder()
which as I've understood is not the correct behavior. For every other child the old ViewHolder is bound in onBindViewHolder()
.
As a side note, onBindViewHolder()
is called too soon, before the animation is finished. Even if dispatchAnimationFinished(holder)
is called after the animation is finished.
Here's the ItemAnimator subclass.
public class CustomItemAnimator extends DefaultItemAnimator {
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder) {
return true;
}
@Override
public boolean canReuseUpdatedViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, @NonNull List<Object> payloads) {
return true;
}
@Override
public boolean animateChange(@NonNull RecyclerView.ViewHolder oldHolder, @NonNull RecyclerView.ViewHolder newHolder, @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo) {
CustomHolder holder = (CustomHolder) newHolder;
CustomView customView = holder.customView;
Animator animator = customView.revealAnimation();
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
dispatchAnimationFinished(holder);
}
});
animator.start();
return false;
}
}
and here's the custom view animation code:
CustomView.java
public Animator revealAnimation() {
return circularRevealView(visibleView, hiddenView);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
private Animator circularRevealView(View visibleView, View invisibleView) {
// get the center for the clipping circle
int cx = visibleView.getWidth() / 2;
int cy = visibleView.getHeight() / 2;
// get the final radius for the clipping circle
float finalRadius = (float) Math.hypot(cx, cy);
// create the animator for this view (the start radius is zero)
Animator anim =
ViewAnimationUtils.createCircularReveal(invisibleView, cx, cy, 0, finalRadius);
visibleView.setVisibility(INVISIBLE);
invisibleView.setVisibility(View.VISIBLE);
// return the animation for later use
return anim;
}
Thanks.
animateChange(...) is called after notifyDataSetChanged(),it means that onBindViewHolder() goes before animateChange(...).and the state of holder has already changed.maybe you can do it this