Android - what is the meaning of StableIDs?
Stable IDs allow the ListView
to optimize for the case when items remain the same between notifyDataSetChanged
calls. The IDs it refers to are the ones returned from getItemId
.
Without it, the ListView
has to recreate all View
s since it can't know if the item IDs are the same between data changes (e.g. if the ID is just the index in the data, it has to recreate everything). With it, it can refrain from recreating View
s that kept their item IDs.
DiffUtils vs stable Ids in recycler view
Long story short, DiffUtill fully replaces stableIds approach.
StableIds is a legacy approach from the times when everybody was migrating from ListView to RecyclerView. Along with ItemAnimator it provided an easy approach to get simple predictive animations out of the box. Predictive meaning that RV could deduce itself which items are added/removed or moved when you just called notifyDataSetChanges without bothering about other callbacks.
Once DiffUtil came along there were no need to RV to deduce that, cause DiffUtills tells RV exactly which items are being moved/added/removed.
I use RV in very tough conditions with dozens of item types, and multiple data updates per second, and spend dozen hours debugging RV and animations internals and didn't notice any significant changes in its scrapping/de-scrapping/1-2-3-steps-layouting behaviour when was trying to add stableIds on top of DiffUtil.
Here's a big piece on how animations worked in circa 2015 pre DiffUtill:
https://www.birbit.com/recyclerview-animations-part-1-how-animations-work/
https://www.birbit.com/recyclerview-animations-part-2-behind-the-scenes/
And a little bit more if you're interested in RV internals:
https://android.jlelse.eu/anatomy-of-recyclerview-part-1-a-search-for-a-viewholder-404ba3453714
Android ListView item: stableIds and yet getting incorrect position
You are right this is a recylcing problem and it has nothing to do with stable Ids. As pointed out by samgak, you should not be invoking the super call:
View v = super.getView(position, convertView, parent);
That will create a whole new View
which you are then modifying the background color. However, since that View
is never returned, it gets tossed to the wind when the method call completes. So basically, your getView
method is doing some extra work that's never used. You should be solely dealing with convertView
. Though, this is not causing the recycling problem you are seeing.
It also looks like you are attempting to show a red background for the sel_vb
. I'm assuming that means selected verb? Know there is a built in mechanism to do this. ListViews
support a method called setItemChecked(position, boolean). Basically you can highlight any position using that method call. Note you'll need to enable a choice mode first. You can change the default blue highlight via styles or by creating your own layout instead of using the Android provided one.
Unfortunately there's not much documentation that really explains how enabling stable Ids affect the ListView
. It's only ever used when enabling a choice mode. It helps ensure the correct item is highlighted/checked during things like screen configurations or mutations to the adapter while something is highlighted/checked. Otherwise, stable ids has nothing to do with View
generation.
Its hard to say exactly what's causing the funky recycling behavior but it's definitely something related to how you populate the text via the Id mapping. Specifically this guy:
for(Map.Entry<String, Long> map : mIdMap.entrySet()){
if(id==map.getValue()){
holder.txt.setText(map.getKey());
break;
}
}
My suggestion would be to completely remove all the Id logic from the getView
method. Instead populate the text by doing:
holder.txt.setText(getItem(position));
One more performance consideration. I suggest looking into implement different view types. For your case, you could have 3 different view types based on this logic:
String item = (String) holder.txt.getText();
//First type
if(item.equals(context.getResources().getString(R.string.ind))||
item.equals(context.getResources().getString(R.string.subj))||
item.equals(context.getResources().getString(R.string.imp))||
item.equals(context.getResources().getString(R.string.inf))||
item.equals(context.getResources().getString(R.string.pt))||
item.equals(context.getResources().getString(R.string.ger))||
item.equals(context.getResources().getString(R.string.gerv))||
item.equals(context.getResources().getString(R.string.sup))){
//Second type
} else if(item.equals(context.getResources().getString(R.string.pres))||
item.equals(context.getResources().getString(R.string.impf))||
item.equals(context.getResources().getString(R.string.fut))||
item.equals(context.getResources().getString(R.string.pf))||
item.equals(context.getResources().getString(R.string.ppf))||
item.equals(context.getResources().getString(R.string.futant))){
} else {
//Third type
}
Then you can create three custom layouts to inflate instead of relying on the built in simple_list_item_1 layout. This improves the View recycling and gets rid of the inflate and later modify logic.
Android: How to make an adapter with stable ids?
Override hasStableIds to return true.
Also the data on your adapter must either override hashCode()
or has some kind of id field to be returned on getItemId
.
What does having BaseAdapter.hasStableIds() return true solve?
hasStableIds() == true
means that item IDs do not change for the underlying data items, not that the data of that item never changes. E.g. the same contact has the same ID regardless where it appears in a list, but its name may change over time--user could edit it, add a nickname, etc. It's possible to imagine displaying data where each item does not have some stable ID (perhaps the data is not yours and you have no way to access a unique ID that is suitable to return from getItemId()
, e.g. not compatible with the long
return type).
In browsing the source code for AdapterView
and AbsListView
, it seems to me that this is mostly useful when you set a choice mode on a ListView/GridView, because then the view can do some more efficient management of checked item IDs and they're visual state, including across data set changes where those checked items may have moved to new positions (but they have the same ID).
Cannot change whether this adapter has stable IDs while the adapter has registered observers
You must set the adapter's hasStableIds to true BEFORE you assign the adapter to your recyclerview.
YourAdapter adapter = new YourAdapter();
adapter.setHasStableIds(true);
myRecyclerView.setAdapter(adapter);
hasStableIds () in Expandable ListView?
Documentation of hasStableIds()
Indicates whether the child and group IDs are stable across changes to the underlying data.
Returns
whether or not the same ID always refers to the same object
It's used when you change the data of the Adapter, everytime you change the data the ExpandableListView
should update it's views to reflect the changes.
If true the ExpandableListView
can reuse the same View
if the ID is the same.
If false it should recreate all the views since it can't have any idea what changes.
The ID which i refer is the ID returned by getGroupId
and getItemId
.
You should override this methods too!
Some questions:
- BaseAdapter: set hasStableIds() to false?
- Android - what is the meaning of StableIDs?
Related Topics
Android Opening a File with Action_Get_Content Results into Different Uri'S
Android Downloadmanager Progress
Access to Google API - Googleaccountcredential.Usingoauth2 VS Googleauthutil.Gettoken()
Server-Side Verification of Google Play In-App Billing Version 3 Purchase
Get a List of Available Content Providers
Make New Activity Appear Behind Old One During Transition
Single Observable with Multiple Subscribers
Take a Screenshot Using Mediaprojection
Android Webview: Download Files Like Browsers Do
Capture Picture Without Preview Using Camera2 API
Android - How to Display a Dialog Over a Native Screen
How to Send Hashmap Value to Another Activity Using an Intent
Analyticsservice Not Registered in the App Manifest - Error
Fragment Inner Class Should Be Static
How to Add Border Around Linear Layout Except at the Bottom