onBindViewHolder() is never called on view at position even though RecyclerView.findViewHolderForAdapterPosition() returns null at that position
This is happening because:
- The views are not added to the recyclerview (
getChildAt
will not work and will return null for that position) - They are cached also (
onBind
will not be called)
Calling recyclerView.setItemViewCacheSize(0)
will fix this "problem".
Because the default value is 2 (private static final int DEFAULT_CACHE_SIZE = 2;
in RecyclerView.Recycler
), you'll always get 2 views that will not call onBind
but that aren't added to the recycler
In your case views for positions 8 and 9 are not being recycled, they are being detached from the window and will be attached again. And for these detached view onBindViewHolder
is not called, only onViewAttachedToWindow
is called. If you override these function in your adapter, you can see what I am talking.
@Override public void onViewRecycled(ViewHolder vh){ Log.wtf(TAG,"onViewRecycled "+vh); } @Override public void onViewDetachedFromWindow(ViewHolder viewHolder){ Log.wtf(TAG,"onViewDetachedFromWindow "+viewHolder); }
Now in order to solve your problem you need to keep track of the views which were supposed to recycled but get detached and then do your section process on
@Override public void onViewAttachedToWindow(ViewHolder viewHolder){ Log.wtf(TAG,"onViewAttachedToWindow "+viewHolder); }
The answers by Pedro Oliveira and Zartha are great for understanding the problem, but I don't see any solutions I'm happy with.
I believe you have 2 good options depending on what you're doing:
Option 1
If you want onBindViewHolder()
to get called for an off-screen view regardless if it's cached/detached or not, then you can do:
RecyclerView.ViewHolder view_holder = recycler_view.findViewHolderForAdapterPosition( some_position );if ( view_holder != null ){ //manipulate the attached view}else //view is either non-existant or detached waiting to be reattached notifyItemChanged( some_position );
The idea is that if the view is cached/detached, then notifyItemChanged()
will tell the adapter that view is invalid, which will result in onBindViewHolder()
getting called.
Option 2
If you only want to execute a partial change (and not everything inside onBindViewHolder()
), then inside of onBindViewHolder( ViewHolder view_holder, int position )
, you need to store the position
in the view_holder
, and execute the change you want in onViewAttachedToWindow( ViewHolder view_holder )
.
I recommend option 1 for simplicity unless your onBindViewHolder()
is doing something intensive like messing with Bitmaps.