androidandroid-fragmentsandroid-recyclerviewandroid-viewholder

ViewHolder returns the old position in RecyclerView


I have a Fragment with BottomSheet behavior that contains a horizontal RecyclerView. RecyclerView is adjusted by SnapHelper to achieve a page effect. It looks something like this:

enter image description here

In the onCreateView event in the Fragment, I set Adapter and LayoutManager:

recycler_view.setAdapter(new MyRecyclerAdapter(getContext(), dataList));
recycler_view.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));

After I set Adapter, the onCreateViewHolder event is triggered in the MyRecyclerAdapter class object, followed by onBindViewHolder:

@Override
public MyRecyclerAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    View view = inflater.inflate(R.layout.fragment_recycler_item, parent, false);
    return new MyViewHolder(view, parent);
}

@Override
public void onBindViewHolder(MyRecyclerAdapter.MyViewHolder viewHolder, int position) {
    /**I do something using position**/
}

When I create this Fragment and configure RecyclerView for the first time-everything works well, onBindViewHolder returns the correct position. But when I flip to the 1st, 2nd, etc. element RecyclerView, and then recreate the fragment, for some unknown reason onBindViewHolder return the old (last) position, although a new Adapter and LineraLayoutManager were created.

To make it clear, I'll break it down into steps:

  1. Creating a Fragment.
  2. Creating an object of the MyRecyclerAdapter class. Set it in RecyclerView.
  3. Creating an object of the LinearLayoutManager class. Set it in RecyclerView.
  4. The onBindViewHolder event in MyRecyclerAdapteris triggered. I get position = 0.
  5. I scroll to the element with position = 2.
  6. I do detach this fragment, and then immediately attach this fragment.
  7. Creating an object of the MyRecyclerAdapter class. Set it in RecyclerView.
  8. Creating an object of the LinearLayoutManager class. Set it in RecyclerView.
  9. The onBindViewHolder event in MyRecyclerAdapteris triggered. I get position = 2.

Question: Why do I get the old position and most importantly how do I fix it?

I read that Android specifically saves the old ViewHolder to use memory more efficiently, but I don't need it.

UPD (Solution): It seems to me that this is a temporary solution (not ideal), but it works. You need to run smoothScrollToPosition(0) after you have installed Adapter and LayoutManager. It is very important to use smoothScrollToPosition(0), not scrollToPosition(0):

recycler_view.setAdapter(new MyRecyclerAdapter(getContext(), dataList));
recycler_view.setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false));
recycler_view.smoothScrollToPosition(0);

UPD.1 (Best Solution): Just use this:

recyclerView.setSaveEnabled(false);

Solution

  • If you detach and reattach the fragment then only its View hierarchy is being destroyed and recreated. Restoring scroll position is part of RecyclerViews state restoration behavior.

    If you want your RecyclerView to always start at scroll 0 you can prevent it from saving its scroll state in the first place by disabling that behavior:

    recyclerView.setSaveEnabled(false);