How to disable animation for ListAdapter
Asked Answered
C

3

29

I am using new ListAdapter, which automatically animates changes. I would like to disable animations or enable/disable it programmatically.

class UserAdapter extends ListAdapter<User, UserViewHolder> {
     public UserAdapter() {
         super(User.DIFF_CALLBACK);
     }
     @Override
     public void onBindViewHolder(UserViewHolder holder, int position) {
         holder.bindTo(getItem(position));
     }
     public static final DiffUtil.ItemCallback<User> DIFF_CALLBACK =
             new DiffUtil.ItemCallback<User>() {
         @Override
         public boolean areItemsTheSame(
                 @NonNull User oldUser, @NonNull User newUser) {
             // User properties may have changed if reloaded from the DB, but ID is fixed
             return oldUser.getId() == newUser.getId();
         }
         @Override
         public boolean areContentsTheSame(
                 @NonNull User oldUser, @NonNull User newUser) {
             // NOTE: if you use equals, your object must properly override Object#equals()
             // Incorrectly returning false here will result in too many animations.
             return oldUser.equals(newUser);
         }
     }
 }
Castalia answered 5/11, 2018 at 8:51 Comment(2)
Try android:animateLayoutChanges="false" on the parent layout?Mason
I tried (android:animateLayoutChanges="false") on parent layout and on recyclerview, but not working.Castalia
B
49

Another solution is to simply remove the item animator altogether.

recyclerView.itemAnimator = null
Befool answered 11/12, 2019 at 6:54 Comment(2)
and then restore the animation by doing recyclerView.itemAnimator = new DefaultItemAnimator()Multiplication
what about ViewPager2?Ecclesiasticus
C
18

You could try to disable or enable animations with setSupportsChangeAnimations on RecyclerView item animator:

SimpleItemAnimator itemAnimator = (SimpleItemAnimator) recyclerView.getItemAnimator();
itemAnimator.setSupportsChangeAnimations(false);
Chamomile answered 5/11, 2018 at 9:7 Comment(2)
Nice one Aaron! Did you do this for Espresso tests?Stevenage
Hey Matt! No I didn't use this for Espresso tests, it should work fine without turning off change animations here :DChamomile
H
2

Solution which bypasses DiffUtil

Setting itemAnimator to null and calling submitList() still runs DiffUtil.ItemCallback in a background thread and doesn't submit the list on the same frame! If you want the same behaviour as calling notifyDataSetChanged(), you can do this:

adapter.submitList(null)
adapter.submitList(newItems)

This is useful if you know the contents are indeed completely different and don't care if you loose the scroll position. Or if you have multiple RecyclerViews that all have to update on the same frame (to reduce screen flashing).

Why does this work?

Looking at the source code of AsyncListDiffer.submitList():

// fast simple remove all
if (newList == null) {
    //noinspection ConstantConditions
    int countRemoved = mList.size();
    mList = null;
    mReadOnlyList = Collections.emptyList();
    // notify last, after list is updated
    mUpdateCallback.onRemoved(0, countRemoved);
    onCurrentListChanged(previousList, commitCallback);
    return;
}

// fast simple first insert
if (mList == null) {
    mList = newList;
    mReadOnlyList = Collections.unmodifiableList(newList);
    // notify last, after list is updated
    mUpdateCallback.onInserted(0, newList.size());
    onCurrentListChanged(previousList, commitCallback);
    return;
}

When you call submitList() the first time, all items are immediately removed. The second time they are immediately inserted, never calling DiffUtil or starting a background thread computation.

Putting it all together

if(animations) {
    adapter.submitList(newItems)
} else {
    recyclerView.itemAnimator = null
    adapter.submitList(null)
    adapter.submitList(newItems) {
        recyclerView.post {
            //Restore the default item animator
            recyclerView.itemAnimator = DefaultItemAnimator()
        }
    }
}
Haight answered 1/12, 2021 at 16:57 Comment(4)
what is animations var? in this line if(animations) {Congratulant
@DrMido it's a parameter you specify. Set it to true if you want animations, false otherwise. Maybe put the code in a fun submitData(newItems: List<T>, animations: Boolean)Haight
I am not sure IF I understand what you mean correctly, but I used AsyncListDiffer not diffUtil and the second parameter is on submitList is @Nullable final Runnable commitCallback) not animation, however when I used adapter.submitList(null) it always moves to the first position of the recycler view, How can I fix that?Congratulant
Yeah, you're right, this always scrolls to top ... This is only good for use cases, where the entire dataset changes, not just a few items.Haight

© 2022 - 2024 — McMap. All rights reserved.