Michalsx
Michalsx

Reputation: 3621

How to disable animation for ListAdapter

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);
         }
     }
 }

Upvotes: 29

Views: 14020

Answers (3)

Remc4
Remc4

Reputation: 1170

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()
        }
    }
}

Upvotes: 2

Nathan Reline
Nathan Reline

Reputation: 1227

Another solution is to simply remove the item animator altogether.

recyclerView.itemAnimator = null

Upvotes: 51

Aaron
Aaron

Reputation: 3894

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

SimpleItemAnimator itemAnimator = (SimpleItemAnimator) recyclerView.getItemAnimator();
itemAnimator.setSupportsChangeAnimations(false);

Upvotes: 19

Related Questions