Android - What Is the Meaning of Stableids

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 Views 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 Views 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



Leave a reply



Submit