2

The android developers guide says that Views in your activity need to have unique ids in order to restore them in onRestoreInstanceState(). This is because onRestoreInstanceState() works by making a SparseArray with ids as keys.

I find this extremely strange because so many standard Android approaches encourage reuse of ids. For example, all of the constructors for an ArrayAdapter take a resourceId as parameter, so that the same xml resource is inflated for all children in a ListView. Similarly if you use <include layout ...> in your xml, you are reusing view ids.

So unless developers aren't actually using the the standard, recommended, approaches I can't see that there are many apps out there actually meeting the requirement that all Views have unique ids.

Is this conclusion wrong? Also, what is the behaviour of onRestoreInstanceState if the ids are not all different, and is this behaviour acceptable?

EDIT

The relevant quotes appear on the page http://developer.android.com/training/basics/activity-lifecycle/recreating.html and read as follows:

Note: In order for the Android system to restore the state of the views in your activity, each view must have a unique ID, supplied by the android:id attribute.

Caution: Always call the superclass implementation of onRestoreInstanceState() so the default implementation can restore the state of the view hierarchy.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • Where are you seeing that? Its not in the documentation for onRestoreInstanceState. It also makes no sense to me as to the best of my knowledge onRestoreInstanceState doesn't do anything for you- you have to restore each element by hand. – Gabe Sechan Dec 15 '14 at 03:36
  • I can't find it now either. It definitely used to say it (as recently as August when I posted a similar question) in big bold letters! This has been bothering me for a while now, but I haven't checked recently so maybe they've changed the implementation. It definitely used to be true (see eg http://stackoverflow.com/q/6045361/3973077 among numerous others finding the same issue). I'd be grateful if an expert could confirm what's going on. – Paul Boddington Dec 15 '14 at 03:54
  • This was never in the documentation of `onRestoreInstanceState()`. The base implementation of that method in `View` is designed to throw an `IllegalStateException` with a message to that effect if it is called with any other concrete parameter than the one it saves and expects: `BaseSavedState.EMPTY_STATE`. This is usually caused by an incorrect implementation of the method in a custom `View`. – corsair992 Dec 15 '14 at 09:54
  • I've now found the quotes I was thinking of - not in the documentation for `onRestoreInstanceState` but in the android documentation all the same. See the link in my updated question. – Paul Boddington Dec 15 '14 at 17:17

2 Answers2

3

AdapterView (the base class for all ViewGroups that utilize an Adapter) is implemented so as to not save or restore the state of it's child Views, as that is the domain of the Adapter and should be handled at that level.

As for the case of a sublayout that is statically included at multiple points, if it has any state to be saved then you will need to either assign unique ids to all stateful Views, or manage the state manually from the Activity or Fragment.

Unfortunately, the ids need to be unique across the whole View structure, instead of just among it's siblings. This is due to the fact that the state of the whole structure is saved inside one single-dimensional SpareArray. Thus if there are multiple stateful Views in the structure sharing the same id, then only the state of the last one in the structure will be saved, and it will be applied to all the Views with that id upon state restoration.

corsair992
  • 3,050
  • 22
  • 33
  • Thank you. That is a very convincing answer. I was being mislead by android's terrible documentation (not for the first time!). The page I link to really ought to mention that the unique id requirement does not apply to the children of an `AdapterView`. – Paul Boddington Dec 16 '14 at 02:03
  • So you're telling that even if I just need 2 items inflated I should use an Adapter? Seems not too nice. – htafoya Jan 12 '23 at 04:52
0

One simple solution can be to assign ID programatically.

Ids are not inmutable, so you may change the ids during your view creation.

Let's say you inflate some views MyCustomView which have some EditText named nameEditText. At the view creation you could have the following:

binding.view1.nameEditText.id = R.id.pageX_field1
binding.view2.nameEditText.id = R.id.pageX_field2

This way the save state will store the values based on the given ids. The restoration of state will happen after the creation of the view.

You may create your ids on a resource file for this purpose:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    
    <!-- PAGE X IDS -->
    <item type="id" name="pageX_field1" />
    <item type="id" name="pageX_field2" />

</resources>
htafoya
  • 18,261
  • 11
  • 80
  • 104