I use a RecyclerView to display a list of items with a list layout. I switch from a list layout to a grid layout, showing just a subset of all the data when in grid layout. This switch uses a different layout XML than when list layout is presented.
All this works well, except that when I scroll, recycled (cached?) list layout views populate the grid, mixed in with proper grid layout view items. In other words, instead of using my layout_grid.xml
for each item's layout in the RecyclerView, I get items using the layout_list.xml
layout, but in a grid format.
This tells me the LayoutManager is working correctly, switching from a list to grid layout. But not all item view items are recreated using the grid xml layout, but rather recycled list layout views are used.
I tried RecyclerView.removeAllViews()
, RecyclerView.removeAllViewInLayout()
, RecyclerView.swapAdapter()
(to force adapter reload), all to no avail.
Update:
If I scroll two positions down in the list, and then switch from list to grid, the first two positions do not go through onCreateViewHolder(), but straight to onBindViewHolder() and are therefore not forced to use the grid layout xml. Instead those first two position items are recycled (I think) and displayed in their list layout format.
Ok now I understand. You use both layouts? There is a method to return the viewtype for a list/grid element (id/position). You have too implement this method correctly that the adapter can recycle the correct layout for the new view. Do you understand what I mean? :)
Use
recyclerView.getRecycledViewPool().clear();
I had the same problem. The solution suggested by Mann works well. An other option would be to set the RecycledViewPool of the RecyclerView to a new instance. That works for me as well.
recyclerView.setRecycledViewPool(new RecyclerView.RecycledViewPool());
But I think overriding the getItemViewType() is the cleaner solution.
It sounds like the accepted answer is the right one for your case, returning a different viewType is probably what you needed to do.
If anyone does need to find a way to clear cached view holders and ended up in this thread, here is what I found that worked.
You need to call setLayoutManager(null)
and setAdapter(null)
. If you want to keep your adapter, you can do something like adapter = getAdapter(); setAdapter(null); setAdapter(adapter);
They key here was to override getItemViewType(int position)
and set it up so that I could check which view type (list or grid) I'm dealing with for that position and then set the appropriate view handling for that position in onbindViewHolder()
. Below, viewFormat
is simply an int that I set to either list or grid when the user taps the menu button to change the format.
@Override
public int getItemViewType(int position) {
if (listData.size() == 0) {
return EMPTY_VIEW;
} else if (viewFormat == Constants.LIST_FORMATTED) {
return Constants.LIST_FORMATTED;
} else if (viewFormat == Constants.GRID_CONDITION) {
return Constants.GRID_CONDITION;
}
return super.getItemViewType(position);
}
A user who posted comments on my original post made me aware of this, but unfortunately those comments were edited out. I thank that user for pointing me in the right direction.
Recyclerview has view caching as well as recycled views pool (view holders pool).
Cache is of views (views with populated data). When LayoutManager tells recyclerview that view is no longer visible and needed, recyclerview checks if that view is still valid for particular position, if it is valid then it is added to cache, so when next time LM asks for the view for that position it returns from cache as it is.
Then what is recycled views pool? When views are removed from cache or are no more valid views, then data related to those views is cleared and only view holder object is kept in pool. Depending on itemType, pool can have view holders of different item types.
Now, in your case, your views are not valid for that particular position as you want to show altogether different view. So, your adapter should tell recyclerview that all the views are invalid. Only way to do is by using notifyDataSetChanged for all items. Recyclerview will remove views from cache and add those to recyler views pool. So now, when LM asks for view, either recycled view(according to itemType) or newly created view will be returned, and adapter will bind data to these views.
Just do this Simple !!
adapter.notifyItemRangeRemoved(0,adapter.getItemCount());
adapter.notifyItemRangeRemoved(0,adapter.getItemCount());
for (int i = 0; i < array.length(); i++) {
JSONObject object = array.getJSONObject(i);
////
data_List.add(data);
}
final int targetCacheSize = 2;
recyclerView.setItemViewCacheSize(Integer.MIN_VALUE);
recyclerView.getRecycledViewPool().clear();
recyclerView.setItemViewCacheSize(targetCacheSize);
来源:https://stackoverflow.com/questions/27951473/recyclerview-how-to-clear-cached-recycled-views