We cache the views in ViewHolder so that too many findViewById() calls could be avoided. In the same way, whether data can be cached in RecyclerView Adapter?
I heard that views are cached in ViewHolder but I am not sure whether data is cached when we use Adapter? If data caching is not provided by RecyclerView Adapter, application can still be crashed if too many calls to data sources occurs?
What exactly you mean my by cache if you meant in the Sense of Image you can Use library like Picasso and perform like this and cache Images. and if you Mean cache in the sense of data your List or Array is already Working as cache.Only visible Views in ListView have Data Rest is Imaginary only data reside within the List or array!
No You can't
In Android RecyclerView, only the number of views which will be visible are created. It is handled by the onCreateViewHolder() method.
And when the user scrolls the list, the views which go out of the visible area are reused by populating it with new data. This operation is carried out in the onBindViewHolder(ViewHolder viewholder, int position) method.
As views are recycled for new data, there is no point of caching the data in viewholders.
By the way if you look at onCreateViewHolder(ViewGroup parent, int viewType), you will notice that there is no position parameter. which means you will not even be able to link your list item data, because the ViewHolder is going to be used for multiple positions in the list.
Related
I have watched a lot of videos on RecyclerView but I am very confused on whether the ViewHolder is an adapter that changes the view or does it really just represent each item that is displayed on the screen.
How can I understand the concept more?
Let's see if this helps.
The general job of any list-style view would be to display a long chain of views, each representing a piece of data, most likely from a list.
Now imagine we consider the simplest implementation, where it draws all those views when it is created and allows you to scroll through them. This is obviously very inefficient for performance as a long list would require a lot of processing all at once.
RecyclerView aims to solve this and only creates enough views to fit on the screen and when scrolling, changes the content of those views seamlessly to reflect more data.
Now these views are created initially as empty blueprints and the RecyclerView wraps them inside something called a ViewHolder, which can not only hold the view but also pointers to different parts of the view, which saves doing even more work every time new data is displayed. Then initially and when scrolling, the RecyclerView 'binds' the relevant data to view holders.
The job of the adapter is to tie this process together and has three methods that require you to provide a concrete implementation:
getItemCount - expected to return how many items there are in the full dataset
onCreateViewHolder - create a view holder representing a generic row
onBindViewHolder - bind data to a view holder, therefore updating the rows content when given the view holder and the position in the dataset that should be bound
I have a recyclerview which populates different types of ViewHolders based on the data provided in the adapter arraylist. In one of the scenarios I have to populate a viewholder that shows HTML text inside a webview. When this viewholder is added, the recyclerview lags while scrolling. If this viewholder is removed then the recyclerview scrolls fine.
Other viewholders need to be update-able since they change UI based on user interactions, and some viewholders add additional objects into the recyclerview when user clicks on the viewholder(expandable recyclerview item). So the position of the viewholders might not be same on every occation.
Is there a way to fix this scrolling issue. Or atleast to pre-load that one particular viewholder and keep it unchanged?
I have a RecyclerView with a custom Adapter to display rectangular elements (about 15, the number can change if the user adds/removes items) in a grid (using Gridlayout Manager).
These elements consist of an ImageView and a TextView. I want to update both Views regularly after receiving new results from network requests (say every 2minutes).
What would be the best way to update these views? How can I identify the specific ViewHolder? How can I access the ViewHolder values of these elements? Would it be best to keep a reference to each ViewHolder in the Activity where I want to change them? Or should I create a new element with the new values and replace the current element with it? What would be considered best practice?
If you are storing your value of image view and text view in an arraylist you can call notifyDataSetChanged on the adapter when you have a change in the dataset.
For example if you have say a Grid object whose attributes include image uri(to set the image) and string(to set in text view) and some Array list say
List<Grid> gridList = new ArrayList<>();
you can update whatever new data you get in the Arraylist and after you are done updating in the array list call
adapter.notifyDataSetChanged();
The best practice is update views and it's data when some data change. You have to do this always. The method
adapter.notifyDataSetChanged
do this for you.
When you update some data you have to tell to the system, because there's an important action named recycle, when you use a view holder you giving the possibility to the system recycle a view, what means views that don't change the date are stored on the view holder, so the list doesn't need to use the
findViewById
again.
I am implementing a ListView using custom adapters. To improve the performance I am returning the newly inflated View if convertView is null, else returning the recycled View.
Now in this process I used the ViewHolder pattern and used the setTag() method of the View. Initially I thought this is some kind of bookmarking but I've not completely understood the use of it. I've checked different blog post where they just used this in the code.
Can someone please explain me the use of the setTag() method?
Basically you can store any kind of object as tag (and cast it back when calling getTag). This can be a simple ID or some complex data. It's some information which you associate with this view.
In the case of lists and the view holder pattern it's a simple object which contains references to views of the tagged view (group). So you don't have to call findViewById every time when you're updating the content of the view. It's just an performance optimization.
Can we store data of list item in the view tag?
No. Because of view recycling you have (e.g.) 10 views which are reused for 1000 list items. Storing data in the tag makes no sense here. It's better to use an custom data object to store the list item state (probably the same array which contains the displayed data) or you persist it right away on list item change.
See also setTag documentation.
I have an Android app that has both CursorAdapter based ListViews (backed by sqlite) as well as custom BaseAdapter based ListViews which are built on the fly from JSON pulled down from a server.
The data displayed in both is identical - an image and a couple of TextViews. My Cursor-based ListView has 3000 rows, the JSON-based ListView has about 30. However, scrolling down the list is significantly faster for the Cursor-based adapter. For the JSON-based lists, the data is all fetched before the rows are made visible. Images for both types of lists are downloaded on-demand.
I have both ListViews configured identically - both with fastScrollEnabled, scrollingCache and smoothScrollbar set to true.
I'm looking for leads on how to go about trying to figure out what's going on here and to potentially fix it so that the JSON-based ListViews as as fast as the Cursor-based ones.
I have a similar thing in my application, except I only have json backed ListView and I also have about 30 items in it (with data constantly changing and animation playing to reflect changes).
It would be much more easier to detect a problem with some provided code from your side, but here are couple of tricks you can sue for optimization.
Reuse convertView that is passed to you as one of the parameters in getView method, it really speeds up the scrolling.
Either create your own row view (by extending some layout, or ViewGroup), or use setTag on a row you return form your getView method. In this tag, you should keep an object that contains references to views in that row, so you wont be searching for them with findViewById everytime a row is requested.
Object can be a simple static class like
private static class ViewHolder {
ImageView image;
TextView text;
}
On the first time (when the convertView is null and you need to create a fresh row) you just create instance of your ViewHolder and set those parameters to refer to parameters from your newly created row (by calling findViewById), and put this instance in row's setTag method.
Next time, when you reuse convertView, just call getTag and in the tag you recieved you'll get references to views in that row, so you won't be needing to call findViewById anymore.
Of course, you might already done all those things.
P.S. I advice you (if you haven't already) to watch Google I/O presentation about ListView's. A lot of useful information there: http://www.youtube.com/watch?v=wDBM6wVEO70