Home » Android » android – RecyclerView Adapter notifyDataSetChanged stops fancy animation

android – RecyclerView Adapter notifyDataSetChanged stops fancy animation

Posted by: admin April 23, 2020 Leave a comment

Questions:

I am building a component based on RecyclerView, allowing user to reorder items by drag and drop.
Once I am on the DragListener side, I need the position it has in the adapter in order to perform correct move, but I only have access to the view.
So here is what I am doing in the adapter view binding :

@Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
    Track track = mArray.get(position);
    viewHolder.itemView.setTag(R.string.TAG_ITEM_POSITION, position);
}

Does it seem correct to you ?
Because if I move an item like this :

public void move(int from, int to){
    Track track = mArray.remove(from);
    mArray.add(to, track);
    notifyItemMoved(from, to);
}

then position tag is not correct anymore, and if I notifyDataSetChanged(), I lose the fancy animation.
Any suggestion ?

How to&Answers:

No, it is wrong. First of all, you cannot reference to the position passed to the onBindViewHolder after that method returns. RecyclerView will not rebind a view when its position changes (due to items moving etc).

Instead, you can use ViewHolder#getPosition() which will return you the updated position.

If you fix that, your move code should work & provide nice animations.

Calling notifyDataSetChanged will prevent predictive animations so avoid it as long as you can. See documentation for details.

Edit (from comment): to get position from the outside, get child view holder from recyclerview and then get position from the vh. See RecyclerView api for details

Answer:

There is a way to preserve fancy animations with just notifyDataSetChanged()

  1. You need to make your own GridLayoutManager with overriden supportsPredictiveItemAnimations() method returning true;

  2. You need to mAdapter.setHasStableIds(true)

  3. The part I find tricky is you need to override you adapter’s getItemId() method. It should return value that is truly unique and not a direct function of position. Something like mItems.get(position).hashCode()

Worked perfectly fine in my case – beautiful animations for adding, removing and moving items only using notifyDataSetChanged()

Answer:

1) You’ll use notifyItemInserted(position); or notifyItemRemoved(position); instead of notifyDataSetChanged() for animation.
2) You can just manually fix your problem – using

public void move(int from, int to){
    Track track = mArray.remove(from);
    mArray.add(to, track);
    notifyItemMoved(from, to);
    ViewHolder fromHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(from);
    ViewHolder toHolder = (ViewHolder) mRecyclerView.findViewHolderForPosition(to);
    Tag fromTag = fromHolder.itemView.getTag();
    fromHolder.itemView.setTag(toHolder.itemView.getTag()); 
    toHolder.itemView.setTag(fromTag);

}

Answer:

You should move your method to OnCreateViewHolder, then notifyItemRemoved(index) works properly.

Answer:

I’m able to maintain the touch animations by adding this to my list item’s outer element

<View
    android:foreground="?android:attr/selectableItemBackground"
...>

Answer:

I fixed it with using ‘notifyItemChanged(int position);’ instead of ‘notifyDataSetChanged();’

My adapter shows fancy animations perfectly and without any lags

Edit: I got position from onBindViewHolder’s position.