I read too many articles about this. I Tried all type of permutation & combination of recyclerview method such as setHasFixedSize(true) ,setNestedScrollingEnabled(false), setItemViewCacheSize(20) etc. But my prediction is the below statement is ture:
The main reason Jank occurs the first time because on the first time it is loading the values onto memory dynamically. while once it is down it already has a few elements pre loaded
When I run my app on android the first time I can see a first scroll is lagging(exactly 5-6 items).Once that first scroll is done the list view is really really fast, I mean faster than anything else.
If I close my app without killing it from background and open it again, It won't lag.
if I "kill" my app and run it again, I get the lag for first 5-6 items.
MY QUESTIONS :
Is there any way to load the values onto memory dynamically before user interaction, Like showing a splash screen and load the layout in background thread? I have tried splash screen but this question is how to load the layout onto memory.
How is the scrolling of youTube,Twitter,Instagram is smooth? How are they load their layout first time onto memory?
Note: Main container is ConstraintLayout with custom background & my hierarchy is totally flat, but 3 buttons have drawable icons and custom background , one material shapable imageview, one slider with custom thumb and custom progress drawable and a frameLayout
Make sure to override:
getItemViewType(…) from docs:
Return the view type of the item at position for the purposes of view
recycling.
The default implementation of this method returns 0, making the
assumption of a single view type for the adapter. Unlike ListView
adapters, types need not be contiguous. Consider using id resources to
uniquely identify item view types.
If you have different view types, return an int that identifies said view types, otherwise no need to override.
getItemId(…) from docs:
Return the stable ID for the item at position. If hasStableIds would
return false this method should return NO_ID. The default
implementation of this method returns NO_ID.
That means the function must return a unique id for every item. Use the id of your objects if there's any, otherwise try with the hashCode:
#Override
public long getItemId(int position) {
return itemList.get(position).hashCode();
}
If implemented properly, use setHasStableIds(true). From docs:
Indicates whether each item in the data set can be represented with a
unique identifier of type java.lang.Long.
getItemCount(…) from docs:
Returns the total number of items in the data set held by the adapter.
Simply the number of items in the list.
#Override
public int getItemCount() {
return itemList.size();
}
Related
I can't fully understand getItemViewType(), I searched on the internet and every search tells how to use it but no one is telling how it works. I want to know why it is used, how it works and if it comes before on create view holder and any help will be appreciated.
I searched on the internet I expected to find full explanation but I found its working.
A RecyclerView can show multiple types of items in it. For example, it can show images and videos in one recycler. These obviously would have different views to display them. This function returns the type of view this item should use to display this item. The type doesn't actually mean anything, it's just used by the recycler to pick the right type of view when it recycles them. When it calls onCreateView, it remembers the type of this item was a 1, and saves it in a pool of views for that type. When it later needs to recycle another item of type 1, it looks in that pool to see if it already has a view, and reuses it if so.
You can number them whatever you want, although using an enum and consecutive integers is generally the easiest and most maintainable path.
If your recycler view only needs to display a single type of view, its ok to just hard code this to always return 1.
I have a RecyclerView.Adapter with hasStableIds(true); and a LinearLayoutManager with reverseLayout(true);
I insert a new item to the object list in the adapter then call NotifyItemInserted(0). The list is reversed and loads from bottom to top with the new item always at the bottom.
The problem is when calling NotifyItemInserted all of the ViewHolders are "Refreshed" / "Reloaded" / "Recreated" - The viewholders are a bit complex and do not want them all to be recreated each time.
When I debug the solution, OnBindViewHolder starts from position 0 (Which is correct) then goes up to position 10 or 11 depending on how many items there are on screen, recreating them all.
I have tried many different settings on the adapter, layoutmanager and the recyclerview itself which none of them work.
I see there is a payload which can be passed in NotifyItemChanged but not in NotifyItemInserted. Maybe with the payload I can check whether the item is already on screen and don't recreate it again, I don't know what to do.
I am using Xamarin.Android but that should not be a problem here.
Removing hasStableIds(true) should solve your problem.
Reason:
When you set hasStableIds to true, the Adapter assumes that every item has a unique id, and it uses the values returned from the getItemId(int position) method to animate data changes for you.
That's an easy way to animate changes without any effort (As long as the getItemId(int position) really returns a unique id per item, otherwise you'll crash).
But in your case you want to animate changes by yourself and control which items will be re-rendered and which will not.
I have a ListView in my app that is used to show a list with 2 types of items. The way it is currently implemented is that I have two different XML layouts for each of the item types, my adapter correctly reports the type and in the getView() method I inflate the appropriate XML according the the type in the specified position.
The problem is that in the vast majority of cases the structure of the list of items is that most of the type 1 items are in the beginning and most of the type 2 items are in the end, so usually at first you see mostly type 1 items, you scroll down and at some point you start seeing type 2 items, and they continue until the end of the list.
All works fine while I scroll until I hit that midpoint. Around that point all the calls to getView() get null passed as the convertView parameter. This makes sense obviously. The problem is that is seems like ListView stores all the previous type 1 views in the recycler, and I will not use them as long as I keep scrolling down since from now on most of the views will be type 2 views.
The views are pretty complex, with custom background and bitmaps on top of it, so I end up with lots of views in memory that I will probably never use.
My question is twofold:
Should I even worry about it? right now I am not in the point where I get OOM exceptions, but will I ever get there or is ListView smart enough to "let go" of some of those views when resources get tight?
If I do need to worry about it, is there a way to explicitly tell ListView to clear up it's recycler, or even disable it somehow?
A possible solution is to use the same XML for both layouts, have two ViewGroups in there and just set the visibility of one of them to GONE, but it seems like a waste to have a fairly complex view hierarchy if I am never going to show it.
Should I even worry about it?
No, as the user is perfectly capable of scrolling up, thereby returning to type 1 rows.
right now I am not in the point where I get OOM exceptions, but will I ever get there or is ListView smart enough to "let go" of some of those views when resources get tight?
Once you start getting OutOfMemoryError messages, this ListView will not be your problem. You only have so many row View structures, and all should be really cheap from a memory consumption standpoint.
One suggestion to deal with two different type of child view in Adapter is using getViewTypeCount method and let the adapter know actually you use two different type of view.
The listView maintains each recycler per each view type (in your case, the number will be 2), so you don't worry to any OOM exceptions and don't need to tell ListView to clear up it's recycler.
For more detailed description,
Check: getViewTypeCount and getItemViewType methods of ArrayAdapter
Code snippet for implementation:
public class SampleAdapter extends ArrayAdapter<String> {
...
#Override
public int getViewTypeCount() {
return 2;
}
#Override
public int getItemViewType(int position) {
//the result must be in the range 0 to getViewTypeCount() - 1.
if( position < 10 )
return 0;
else
return 1;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
switch( getItemViewType(position) ){
case 0:
//do something for type1 view.
break;
case 1:
//do something for type2 view.
break;
}
return convertView;
}
}
I would not worry too much when having only 2 view types.
If you want to optimize it, I suggest not having a very complex layouts and instead use custom View and do drawing of the Bitmaps yourself. A bit more complex task, but will bring better UX when going through midpoint.
I'm trying to show an animation with all Views that I've created from an adapter. When I scroll down, it shows the animation correctly, but when I scroll up, I see these Views recreate themselves and show the animation again. Then, when I scroll down, it happens again.
My assumption is that the mechanism of creating a View from an adapter is to load the View into memory; just the group of Views which are on screen right now (but above and below views are not loaded into memory). These will be loaded again when I scroll to these views, right?
Is there any way to fix this problem?
PS: Sorry for my English, I hope you understand my problem.
My assumption is that the mechanism of creating a View from an adapter
is to load the View into memory; just the group of Views which are on
screen right now (but above and below views are not loaded into
memory)
That's somewhat correct: a ListView will not try to visualize any data that isn't (at least partially) visible. It also 'recycles' views, meaning that any view that isn't currently used to present data to the user and is of the same 'type' as the next data item, may get reused.
Hence you shouldn't rely on persisting data with or make any assumptions about the existence of particular views. In stead, use something that's separate from the views; e.g. the dataset you're visualizing.
Quite often, you'll supply a list of POJOs to a BaseAdapter or ArrayAdapter. You could simply add a boolean to the POJO indicating whether it should animate or not, and change that whenever the animation for that particular item finishes. Alternatively, you could keep track of these values in a separate collection (which is probably the more straightforward approach if you're dealing with a Cursor as data source rather than POJOs).
I have a ListView that displays a list of items, each of which has an icon and a few bits of text.
I am making use of the "convertView" parameter of the ListAdapter.getView() method, altering an existing view rather than creating a new one when the parameter is non-null.
I had expected the ListView to recycle old views only after they had scrolled out of the visible viewport, but this appears not to be the case. It appears that the ListView is providing the same object in the "convertView" parameter on each invocation of ListAdapter.getView(). The single view is rendered to the screen, and then sent in again on the next call to getView().
This poses a significant problem for me, as I wish to modify previously rendered views. I have a background thread retrieving the icons for items, which takes "considerable" time and would be an unacceptable user interface burden to place within the ListAdapter.getView() view rendering code.
Is there any means to make the ListView not reuse views which are currently displayed on the screen? I'd like to realize the performance/efficiency gains of view reuse and be able to load the icons in a background thread.
I think you're having a similar problem I had for a while: Old items visible a while, in listview or gridview, when recycling
You have to "reset" the recycled items of the list at the beginning of getView(). Set them back to progress bar, or invisible, whatever the initial state is. Until they fetch the correct data.
I've found the issue is that Android is creating an additional, never-rendered temporary view for use in layout/measurement under certain conditions. My assumption that this view was rendered to the screen was not correct.