RecyclerView - get all existing views/viewholders
In adapter class:
Boolean isVisible = false;
public CustomAdapter(boolean isVisible) {
this.isVisible= isVisible;
}
@Override
public void onBindViewHolder(ViewHolder holder, final int position) {
...
if (isVisible){
//setVisibility(View.VISIBLE)
}else{
//setVisibility(View.INVISIBLE
}
}
public void updateVisibility(boolean newValue){
isVisible= newValue;
}
In Activity
where you want to update the value where the adapter is instantiated:
adapter.updateVisibility(false);
adapter.notifydataSetChanged();
- 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.
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)