I have created a RecyclerView and have set the proper adapter and LinearLayoutManager. I'm able to access my ViewHolders just fine outside of the onCreate method. During my research of the problem, it appears that in order to access a ViewHolder, the LinearLayoutManager must finish calculating the positions of the views before you're able to use either the findViewHolderForAdapterPosition or findViewHolderForLayoutPosition methods.
I would like to modify only one of the views inside of the many viewholders I add to the RecyclerView.
I'm currently attempting (and failing) at accessing the views/viewholders in any of the Activity life cycle methods (onCreate, onStart, onResume). I see that LinearLayoutManager has a onLayoutCompleted method, but am unsure how to make use of that for my situation.
Is there anyway to gain access to either the views or the viewholders inside the RecyclerView during one of the Activity life cycles or somehow figure out when onLayoutCompleted is called by the LinearLayoutManager? Is there another way around this?
Theoretically I could add all the viewholders into a list inside the adapter and access it that way, but that doesn't seem quite so clean.
Try to hold a list of models inside of the adapter. If you need to change the view, just change the model and call RecyclerView.Adapter.notifyItemChanged(int p) or notifyDataSetChanged().
Related
In my case I have a list of objects and each object has a list of other objects, like:
class Parent(val title, val childs: List<Child>)
class Child(val title)
The above code is an example and there are a lot more fields to implement, like images (for the sake of the question lets keep it simple).
Now what I want is:
To use custom layout for both parent and child items
The child items must be displayed inside the parent item
Based on the above, I decided to use a RecyclerView inside a RecyclerView.
Some other options are:
The parent RecyclerView will be scrollable but the child RecyclerView will have fixed height (so there is no need to implement nested scrolling).
Both RecyclerViews have a vertical orientation.
When clicking the parent item the visibility of the child RecyclerView will be toggled (this is not very relevant to the actual question, but just to get an idea of what I have in mind).
Using my poor skills, I would initialize the child RecyclerView inside the parent RecyclerView.ViewHolder and onBindViewHolder method (of the parent RecyclerView.Adapter) I would assign the LayoutManager and initialize/assign the child RecyclerView.Adapter to the child RecyclerView.
I would also implement a Listener that when clicking a child item, a callback will be called containing two parameters, the index of parent and index of child.
I know how to implement what I described above, the actual question is if this is "correct"
or better say, the best practice.
As far as I can think, this implementation seems dirty and not very optimized (maybe I am wrong). I want you to explain me if there is better way to implement what I described above (with examples if possible). If not, providing an explanation why there is no better way will be also useful.
There are multiple ways to go on about this.
The method I would use is the one you described above, however, you should be careful so that you "set up" Child's RecyclerView in the init block of the Parent's ViewHolder, so you don't do too much work on the binding process of the Parent's items binding process. And, I would use this method, because it is easier to implement the:
When clicking the parent item the visibility of the child RecyclerView will be toggled (this is not very relevant to the actual question, but just to get an idea of what I have in mind).
feature, so this is kinda relevant, in the aspect that you can have a harder time to implement it using the next method.
Another way of doing this, is by implementing an adapter that takes into consideration the RecyclerView.Adapter's viewType, which you can use to distinguish between Parent and Child model types so you use the corresponding ViewHolder for each type, making the list look like a list with clear sections, the section headers of which are the Parent's model. This method is great for making a list with sections, however, making a toggle functionality requires careful considerations on how you want to hide the ViewHolders by looking for them specifically or using extra variables to save the state of the sections and careful conditions on the RecyclerView.Adapter's ViewHolder's bind() method, which may or may not cause a headache.
So basically I would use the first method. Hope this helps!
When use RecyclerView, Does it matter how many times setAdapter() has been used?
Or should setAdapter be used only once?
and Is it okay to use setAdapter after adding items to the adpater?
Or should the setAdapter work before adding items to the adapter?
General practise is to call RecyclerView::setAdapter once per instantiation of the RecyclerView then use the RecyclerView.Adapter<VH> for updating the underlying List<T> data set then calling methods like Adapter::notifyDatasetChanged.
Recyclerview.Adapter also allows updating individual rows through other methods : https://developer.android.com/reference/androidx/recyclerview/widget/RecyclerView.Adapter
More modern techniques include DiffUtil or AsyncListDiffer which uses DiffUtil or manual implementations to abstract working out individual row changes between data set updates. This is the most efficient mechanism as it only needs to "rebind" changed data on screen, rather than rebinding all views.
If you intend to change the type T of the underlying data set then you can call RecyclerView::setAdapter more than once, as you are fundamentally changing the adapter data set type. This however is an edge case.
Unless you are switching different adapters on the same RecyclerView, it's recommended you call setAdapter once (even with an empty list of elements). Then, when you update the list of elements, you can call adapter methods like notifyDataSetChanged and other similar methods.
Well, if you are setting the same adapter instance multiple times just to refresh then, please take a look at RecyclerView.Adapter#notifyDataSetChanged()
A better approach would be 'setAdapter work before adding items to the adapter' and then you can add, remove, and modify adapter data items. Then you can notify the adapter.
This question already has answers here:
Android Recyclerview vs ListView with Viewholder
(7 answers)
Closed 3 years ago.
I was reading about the difference b/w recyclerview and listview and found out that recyclerview is faster than listview.
I tried to search online but not found any satisfactory answer I know it is used ViewHolder pattern and Notifying adapter but what does it does intearlly so it is faster?
Recycler View you could say is an efficient way to create list of views.
If you have 1000 items like ur contact list , and If ur visible screen can show only 10 items at once, it will Create only 10+1 (or +2) Views and as u scroll , items/views that left will be reused (not create) to show new data.
Recycler View by default does this, where as List View by default doesn't do.
There are some differences between these two views.
ListView is a bit heavy and it has a lot of responsibilities. Whenever we have to handle the list, such as to configure it in some way, the only way to do this is through the ListView object or inside the adapter.
A lot of bad things in the ListView were fixed or changed in the RecyclerView. It’s more efficient by default, the layout is separated and we have more possibilities over the data set inside the adapter.
These are some crucial differences between ListView and RecyclerView:
1 ViewHolder
The ViewHolder pattern allows us to make our list scrolling act smoothly. It stores list row views references and, thanks to this, calling the findViewById() method only occurs a couple of times, rather than for the entire dataset and on each bind view.
The RecyclerView’s adapter forces us to use the ViewHolder pattern. The creating part (inflating the layout and finding views) and updating the views is split into two methods — onCreateViewHolder() and onBindViewHolder().
The ListView, on the other hand, doesn’t give us that kind of protection by default, so without implementing the ViewHolder pattern inside the getView() method, we’ll end with inefficient scrolling in our list.
2 LayoutManager
The LayoutManager takes responsibility for layouting row views. Thanks to this, RecyclerView doesn’t have to think about how to position the row view. This class gives us the opportunity to choose the way that we want to show the row views and how to scroll the list. For example, if we want to scroll our list vertically or horizontally, we can choose LinearLayoutManager. For grids, it is more suitable to choose the GridLayoutManager.
Previously, with the use of the ListView, we were only able to create a vertical-scrolling list, so it wasn’t that flexible. If we wanted grids on our list, we had to choose the other widget for that — GridView.
3 ItemDecoration
A duty of the ItemDecoration is simple in theory – add some decorations for the list row views – but in practice, it’s that simple to implement if we want to create a custom one. In this case, we should extend the ItemDecoration class and implement our solution. For example, the RecyclerView list has no dividers between rows by default and it’s consistent with the Material Design guidelines. However, if we want to add a divider for some reason, we can use DividerItemDecoration and add it to the RecyclerView. In case we use the ListView, we have to figure out rows decorations by ourselves. There is no helper class like ItemDecoration for this widget.
4 ItemAnimator
The last but not least component of RecyclerView that I want to mention is ItemAnimator. As we can expect, it’s handling row views animations like list appearance and disappearance, adding or removing particular views and so on. By default, RecyclerView’s list animations are nice and smooth. Of course, we can change that by creating our own ItemAnimator, which is also not that easy. To make it easier, we should extend the SimpleItemAnimator class and implement the methods that we need (just add animations to a ViewHolder’s views). To be honest, implementing animations on the ListView was a pain. Again, we had to figure out how we want to handle them.
5 Notifying adapter
We have a couple of cool notifiers on the RecyclerView’s adapter. We are still able to use notifyDataSetChanged() but there are also ones for particular list elements, like notifyItemInserted(), notifyItemRemoved() or even notifyItemChanged() and more. We should use the most appropriate ones for what is actually happening, so the proper animations will fire correctly.
Using ListView, we were able to use just notifyDataSetChanged() on the adapter and then had to handle the rest ourselves, again.
Because of ViewHolder Pattern.
Thats was the simplest answer. Now for some details.
What recycler view does is what it's name indicates "Recycle", yes it recycles items, and it does it with the help of ViewHolder Pattern.
By Using ViewHolder we do-not need to call findViewByID() every time we go through getView()method. The reference for all rows are stored in-memory. This increases the performance significantly, as findViewByID()is a heavy process.
Hope this clears your confusion.
I have a mix of 10-15 custom views and fragments to be shown in a vertical list. I am not sure if RecyclerView has any advantage in scenarios where all views are dissimilar. RecyclerView seems to add lot of boiler-plate code, and I think the only advantage I would get is easier enter/exit animation.
My custom views/fragment also make web-service call on being created. We don't cache web-requests for business reasons. My understanding is that RecyclerView would trigger these web-service calls on each binding, resulting in redundant calls and visible latency. Comparatively ScrollView should load the views once, and it keeps them all in memory, avoiding multiple calls.
Is my understanding correct ? I need some help understanding performance implications with ScrollViews, in the given scenario.
ScrollView
With a ScrollView, all of its subviews will be created at once, regardless of visibility on screen. If using a ScrollView for your solution, you'll probably want to "listen" for when its subviews become visible to update their content, using placeholders initially. You could also build something that will fetch the content in a background thread. This may get more complex than you want very quickly.
RecyclerView
A RecyclerView provides the advantage of deferring creation of child views until they become visible automatically, and can re-use child views with common layouts.
By using different "item view types" for each of your children, you'll disable the "recycling" part of RecyclerView, but still get the benefit of deferring the creation of views until they are scrolled into view.
RecyclerViews do provide a fairly structured pattern for you to work with via the Adapter and ViewHolders. Though not personally familiar with it, RecyclerView also has a RecyclerView.ViewCacheExtension which is intended to give the developer control over caching of views.
Overall, the advantage of late binding (don't create and load views that might never be viewed) and the flexibility of the RecyclerView will probably yield good results for you.
First of all you have to decide what you are using View or Fragment or maybe both. Don't compare View with Fragment there is a common misconception about these two, they are not similar, actually a Fragment is close to an Activity in terms of architecture and implementation.
Second, can you reuse some of these View/Fragment, if yes, then RecycleView can help you a lot.
After you decided about the topics above:
My understanding is that RecyclerView would trigger these web-service
calls on each binding
No, this is not true, the binding method is called whenever a new item is displayed (reused or newly created), you can implement adapter to perform the web API only once on an item, this is your choice.
I always go for RecycleView/ListView whenever possible, it helps to reduce the memory footprint and can reduce the implementation. In some cases, where there is no huge memory usage on views and I can't reuse some of the implementation, then I go for ScrollView, but I think twice before implementing it.
So I have an activity with RecyclerView and I want to change TextView of every item in the RecyclerView by pressing button that has onClickListener() in the activity.
I'm wondering what is better in terms of performance:
Use notifyDataSetChanged ones.
Use loop with condition like int i is less than List.size() where notifyItemChanged would be called few times.
In both cases I create boolean variable in RecyclerView Adapter which is used by onBindViewHolder to know how to update item. By default it's false and after button click it becomes true so onBindViewHolder updates item in different way.
Also I would like to know if this approach is suitable at all.
If you are simply updating one part of the view, use the notifyItemRangeChanged()or notifyItemChanged() instead of notifiyDataSetChanged(). The difference here has to do with structural changes vs item changes. This is on the android developers RecyclerView.Adapter documentation found here.
Here is another tidbit on the differences between the two types of changes:
There are two different classes of data change events, item changes
and structural changes. Item changes are when a single item has its
data updated but no positional changes have occurred. Structural
changes are when items are inserted, removed or moved within the data
set.
This is taken from that aforementioned page,
If you are writing an adapter it will always be more efficient to use
the more specific change events if you can. Rely on
notifyDataSetChanged() as a last resort.
So just to clarify use notifyDataSetChanged() as a last resort, and instead ask yourself if you can preform one of these methods instead, and if you can use it instead:
notifyItemChanged(int)
notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeChanged(int, int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)
which makes sense because notifyDataSetChanged() will pretty much try to redraw everything based on the data and make no previous assumptions on it, while the other methods will just look for changes. That means the adapter has to do a lot more work that is not necessary. This is what notifyDataSetChanged() does:
This event does not specify what about the data set has changed,
forcing any observers to assume that all existing items and structure
may no longer be valid. LayoutManagers will be forced to fully rebind
and relayout all visible views.
This also makes sense to use the incremental or range approach, because you are changing the text, you need to go get each new text and when you do that you should tell the adapter you changed it. Now, if you do a button click and get all new text values, and create a new list or something then call heavy notifyDataSetChanged().
I would definitely call notifyDataSetChanged() if all of the data items ar no longer valid. When you call notifyItemChanged(mPos), it is equivalent to a call to notifyItemRangeChanged(mPos, 1), and every time it is called, requestLayout() is also called. On the other hand, when you call notifyDataSetChanged() or notifyItemRangeChanged(0, mList.size()), there is only one call to requestLayout().
Your question should now be, what is better, a call to notifyDataSetChanged() or notifyItemRangeChanged(0, mList.size())? For that one I don't have an answer.
I've noticed that notifyItemChanged(mPos) triggers onBindVieHolder for corresponding position even it's currently not visible.
For me, calling it in a loop for all elements was more costly than notifyDatasetChanged which redrawn only visible ones.
So be careful with large datasets.