Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android RecyclerView Creates and Binds all the views on Dataset change

I have a very large app in which I have used RecylclerViews almost everywhere, I know how to implement RecyclerViews and I did not have any problem, ever ! but lately I came across a really bad lag on one of the RVs, (let me assure you my bindview methods are perfectly good, I load all the images in the asynctasks and ... ), and that is on first showing the RV I get an ANR !

after hours of debuging, I found out that each time the dataset changes (notifyDataset()), including the very first time that the RV populates, all of the views are both created and bound (yes like 200 times the onCreateView() is called, which is just the opposite of the RecyclerView's philosophy!) Once all the views are created and bound, the app starts functioning normally, I mean no more onCreateView() and as required onBindView() calling, with no lag whatsoever.

I know it's really hard to find the reason, but I thought maybe there is an obvious setting or ... which causes this or some past experience from SO users may help me.


EDIT

apparently the reason is due to my XML and how I made the RV to fill the free area, as when I set it's height to a specified value (eg. 400 dp) everything works fine, but either if I use LinearLayout and layout_weight or doing same with RelativeLayout (set below a aligned-top element and above a align-bottomed element) recreates the issue.

so what I can say is this issue happens when the height of the RV is needed to calculate dynamically and I guess it creates all of it's childs to calculate it's height, which obviously is a bug in the support library (com.android.support:recyclerview-v7:23.4.0 at the time)

anyone has a workaround ? or perhaps can tell me my mistake ?


Solution

for future googlers

It seems that the solution is to use setAutoMeasureEnabled(false); on the LayoutManager. this method has been introduced in the 23.2.0 version of support library and is supposed to be helpful to use wrap_content as the layout params of the RV items, any way I overcame my problem with this method, and am using wrap_content for my item heights with no problem,

like image 317
Muhammad Naderi Avatar asked May 16 '16 12:05

Muhammad Naderi


3 Answers

The onBindViewHolder is the one which will be called to show the view. The first time you set the adapter onBindViewHolder() will not be called 200 times. It will only be called for the number of items visible. Fox example, if there are 4 items visible the onBindViewHolder will be called 4 times, as you scroll onBindViewHolder will be called depending on how many items need to be drawn.

If some data is changing and you use notifyDatasetChanged() then all the views will be redrawn. If you are inserting an element and want to refresh the adapter use something like

adapter.notifyItemInserted(position);

Or for delete

adapter.notifyItemRemoved(positionOfObject);
adapter.notifyItemRangeChanged(positionOfObject, arrayList.size());

It's much more efficient

like image 169
Shashank Udupa Avatar answered Nov 07 '22 20:11

Shashank Udupa


The Recycler View should call OnCreateViewHolder(...) and OnBindViewHolder(...) only for the rows that are visible when it's created. If you need to refresh a row in the list, when an image loads or something like that, you should avoid using notifyDataSetChanged(). Instead you can use one of the invalidateChild(...) methods. To find the right child to invalidate you can use findViewHolderForAdapterPosition(int position). Another way to do it is to user the adapter's notifyItemChanged(int position) method.

like image 43
Veselin Todorov Avatar answered Nov 07 '22 20:11

Veselin Todorov


I have face similar issue, for device running on android 5 adding all elements in list and its creating problem.

I Set height to wrap_content for recyclerview when I change it fix height it working smoothly.

like image 32
dev_swat Avatar answered Nov 07 '22 20:11

dev_swat