I've been dealing with a strange behavior in Recycler View, if the view count in the adapter is small, in my case with size 5, the views are not being recycled and onBindViewHolder is not called when the view is scrolled back to screen. If i increase the size to 10 views for example, then recycling starts working and onBindViewHolder is called every time a view enters the screen.
XML
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipeRefresh"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<!-- RECYCLER VIEW -->
<android.support.v7.widget.RecyclerView
android:id="@+id/eventsList"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v4.widget.SwipeRefreshLayout>
Recycler view initialization
LinearLayoutManager llm = new LinearLayoutManager(getActivity());
llm.setOrientation(LinearLayoutManager.VERTICAL);
eventsList.setLayoutManager(llm);
//Sets endless listener
scrollListener = new EndlessRecyclerOnScrollListener(llm,3,mLastDownloadedPage) {
@Override
public void onLoadMore(int current_page) {
Timber.d("Loading date for page: " + current_page);
mLastDownloadedPage = current_page;
//If it's showing cached don't download
if(showingCached)
return;
//Shows snackbar loading view
showSnackbarLoadingView();
//Gets events
getEvents(false);
}
};
//eventsList.addOnScrollListener(scrollListener);
//Set the adapter
mAdapter = new EventListAdapter(this,getActivity().getApplicationContext());
eventsList.setAdapter(mAdapter);
The Adapter
@Override
public EventHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
Timber.d("Create view holder pos: "+i);
View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_events_row, viewGroup, false);
return new EventHolder(v,mListener);
}
@Override
public void onBindViewHolder(EventHolder eventHolder, int i) {
if(mBindListener != null)
mBindListener.onBind(eventHolder,i);
Timber.d("Bind view holder pos "+i);
Event event = eventList.get(i);
eventHolder.bindEvent(event);
//Sets the date
setDate(eventHolder.date, event);
//Sets the location
setLocation(eventHolder.distance,event);
}
In the screen there are only 3 visible views, with a size of 5, when i scroll down i can see that the createViewHolder
and bindViewHolder
is called for position 4 and 5, but when i scroll to top they are not recycled when exit the screen. I can confirm that because by overriding
@Override
public void onViewRecycled(EventHolder holder) {
super.onViewRecycled(holder);
Timber.d("onViewRecycled: "+holder.name);
}
no message is logged. However if the view count is increase for example with size 10, everything works fine, the views are recycled and the onBindViewHolder
is called for every position.
Is this normal behavior ? If it's how can i save the state of an item view, for example activated state, i used to save the active position in a variable and then used this line to activate the view when it enters the screen using the bind method
holder.itemView.setActivated(pos == lastSelectedPos);
Simple answer: You should use RecyclerView in a situation where you want to show a lot of items, and the number of them is dynamic. ListView should only be used when the number of items is always the same and is limited to the screen size.
RecyclerView does not allocate an item view for every item in your data source. Instead, it allocates only the number of item views that fit on the screen(Viewport) and it reuses those item layouts as the user scrolls.
addItemDecoration(headerDecoration); The decoration is also reusable since there is no need to modify the adapter or the RecyclerView at all.
It is normal behavior, but you can tweak it with:
recyclerView.setItemViewCacheSize(int);
About the second part of the question - you're right! Store the "activated" position as a variable in your adapter. In onBindViewHolder() do something like:
holder.itemView.setActivated(holder.getAdapterPosition() == lastSelectedPos);
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