Reputation: 3621
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
Reputation: 1170
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).
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.
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
Reputation: 1227
Another solution is to simply remove the item animator altogether.
recyclerView.itemAnimator = null
Upvotes: 51
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