Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

RecyclerView - get all existing views/viewholders

I want to update the RecyclerView while it's displaying data, in my case, I show images with or without labels.

Defaultly I set the visibility of the label when I create the view holder and that's fine, but I want the user to change the labels visibility through the menu while the RecyclerView is shown, so I want to manually update the visibility for all existing views in the RecyclerView.

Can I somehow get all existing Views? I need all, not only the visible ones, I don't want that a later recycled View is not updated...

like image 937
prom85 Avatar asked Jan 12 '16 11:01

prom85


People also ask

How do I get all views in RecyclerView?

First you need to get all the views' indices that are shown, and then you need to go over each, and use the viewHolder of each view: final int firstVisibleItemPosition = layoutManager. findFirstVisibleItemPosition(); final int lastVisibleItemPosition = layoutManager.

When onViewRecycled is called?

onViewRecycled() : Once a ViewHolder is successfully recycled, onViewRecycled gets called. This is when we should release resources held by the ViewHolder.


2 Answers

First you need to get all the views' indices that are shown, and then you need to go over each, and use the viewHolder of each view:

final int firstVisibleItemPosition = layoutManager.findFirstVisibleItemPosition(); final int lastVisibleItemPosition = layoutManager.findLastVisibleItemPosition(); for (int i = firstVisibleItemPosition; i <= lastVisibleItemPosition; ++i) {     ViewHolder holder = (ViewHolder) mRecyclerView.findViewHolderForAdapterPosition(i);     ...     } 

EDIT: seems it doesn't always return all ViewHolders you might want to handle. This seems like a more stable solution:

for (int childCount = recyclerView.getChildCount(), i = 0; i < childCount; ++i) {    final ViewHolder holder = recyclerView.getChildViewHolder(recyclerView.getChildAt(i));    ...    } 

Note: on some cases, you might want to set the number of Views being cached to be large enough so that you will always get the same views recycled, instead of new ones. For this, you can use something like that:

fun RecyclerView.setMaxViewPoolSize(maxViewTypeId: Int, maxPoolSize: Int) {     for (i in 0..maxViewTypeId)         recycledViewPool.setMaxRecycledViews(i, maxPoolSize) } 

Example usage:

recyclerView.setMaxViewPoolSize(MAX_TYPE_ITEM, Int.MAX_VALUE) 
like image 151
android developer Avatar answered Sep 17 '22 02:09

android developer


  • Updating the whole adapter list size with notifyDataSetChanged() is not convenient.
  • Getting all the current RecyclerView childs (ie. all visible items) is not enough: there are bound view holders that are not visible: after loading at startup usually there are 2 or 3 items bound over the last visible position (depending by your LayoutManager).

I just experimented watching the logs i put, that RV binds more items that are not visible, indeed I found no way to retrieve them. I ended up to cache in a Set or List those ViewHolders in onBindViewHolder() and to uncache them in onViewRecycled():

private Set<AbstractViewHolder> mBoundViewHolders = new HashSet<>(); private int mVisibility = View.VISIBILE;  // So you can have access from the Activity/Fragment public Set<AbstractViewHolder> getAllBoundViewHolders() {     return Collections.unmodifiableSet(mBoundViewHolders); }  @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List payloads) {     // Binding code     ...     holder.view.setVisibility(mVisibility);      // Caching VH     mBoundViewHolders.add((AbstractViewHolder) holder); }  @Override public void onViewRecycled(RecyclerView.ViewHolder holder) {     // Uncaching VH     mBoundViewHolders.remove(holder); }  // Your method becomes public void updateVisibility(int visibility) {     mVisibility = visibility;     for (AbstractViewHolder holder : mBoundViewHolders) {         holder.updateVisibility(visibility);     } } 

Implement your interface or abstract ViewHolder so you can call your method. In this way you have access to your ViewHolder content.


This is what I do in my FlexibleAdapter to unselect the items without invoking the notifyItemRangeChanged(). And if user has animations, all are executed, plus the background changes too when I invoke my method toggleActivation().

If you know a better way to retrieve all bound VH, let me know.

like image 28
Davideas Avatar answered Sep 20 '22 02:09

Davideas