Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView Adapter notifyDataSetChanged stops fancy animation

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 ?

like image 295
elgui Avatar asked Dec 04 '14 17:12

elgui


People also ask

What does notifyDataSetChanged do in RecyclerView?

notifyDataSetChanged. Notify any registered observers that the data set has changed. There are two different classes of data change events, item changes and structural changes.

What is setHasFixedSize in RecyclerView?

setHasFixedSize(true) means the RecyclerView has children (items) that has fixed width and height. This allows the RecyclerView to optimize better by figuring out the exact height and width of the entire list based on the your adapter.

What does notifyDataSetChanged do?

Notifies the attached observers that the underlying data has been changed and any View reflecting the data set should refresh itself.

What is viewType in onCreateViewHolder?

onCreateViewHolder(parent: ViewGroup, viewType: Int) In onCreateViewHolder, we will inflate the view that we will be using to hold each list item. This method will be called when RecyclerView needs a new ViewHolder of the given type to represent. The params are the Viewgroup parent and int viewType.


2 Answers

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

like image 179
tochkov Avatar answered Sep 19 '22 21:09

tochkov


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

like image 31
yigit Avatar answered Sep 20 '22 21:09

yigit