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...
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.
onViewRecycled() : Once a ViewHolder is successfully recycled, onViewRecycled gets called. This is when we should release resources held by the ViewHolder.
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)
notifyDataSetChanged()
is not convenient.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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With