Why does Android change the value of EditTexts with same id?

There is a different possibility, just change the id of the edit text, for example,

mEditText.setId((parentView.getId()+editTextPosition+someFinalNumber));

Or if it is a EditText inside some custom Layout:

mEditText.setId((this.getId()+someFinalNumber));

In this way all EditTexts will have a different id and the text will be restored correctly.


It can simply be fixed by setting android:saveEnabled="false" in the EditTexts Layout definition. Of course you need to make sure that the content is saved/restored yourself. So this is an non-intuitive work around -- but it works for my case. None the less the entire thing looks like an Android bug:

A nice feature of the Android layout system is that

An ID need not be unique throughout the entire tree [...]

as stated in the Android documentation. This makes code and layout reuse much simpler and is heavily used by developers. I think the save/restore instance state implementation for views uses the view's ID as the key to store it's state, hence it relies on uniqueness in the entire tree. WTF?

Update

I have added a ListView to the example at GitHub which demonstrates that the ListView almost certainly uses a similar workaround to prevent EditTexts to run into this problem. As can be seen, text which is entered into an EditText inside a ListView is not automatically restored.


Greetings from the future. Sadly Android framework developers still like to laugh at our expense. Thanks to our bright scientists, we have come up with a bit better solution. See below (yes we still use java in the future). You can find the original "research paper" here.

private static final String KEY_SUPER = "superState";
private static final String KEY_CHILD = "childState";

@Override
protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
    // Don't call super, to disable saving child view states
    dispatchFreezeSelfOnly(container);
}

@Override
protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
    // Similar as above, but other way around
    dispatchThawSelfOnly(container);
}

@Override
protected @Nullable Parcelable onSaveInstanceState() {
    val bundle = new Bundle();

    val superState = super.onSaveInstanceState();
    bundle.putParcelable(KEY_SUPER, superState);

    val childStates = saveChildViewStates();
    bundle.putSparseParcelableArray(KEY_CHILD, childStates);

    return bundle;
}

private SparseArray<Parcelable> saveChildViewStates() {
    val childViewStates = new SparseArray<Parcelable>();
    for(val view : getChildViews())
        view.saveHierarchyState(childViewStates);
    return childViewStates;
}

@Override
protected void onRestoreInstanceState(Parcelable state) {
    val bundle = (Bundle) state;
    val superState = bundle.getParcelable(KEY_SUPER);
    super.onRestoreInstanceState(superState);

    val childState = bundle.getSparseParcelableArray(KEY_CHILD);
    restoreChildViewStates(childState);
}


private void restoreChildViewStates(SparseArray<Parcelable> states) {
    for(val view : getChildViews())
        view.restoreHierarchyState(states);
}

private Iterable<View> getChildViews() {
    int childCount = getChildCount();

    return () -> new Iterator<View>() {
        private int index = 0;

        @Override
        public boolean hasNext() {
            return index < childCount;
        }

        @Override
        public View next() {
            val view = getChildAt(index);
            if(view == null) {
                throw new RuntimeException("View was null. Index: " + index +
                        " count: " + childCount + ".");
            }
            Objects.requireNonNull(view);
            index++;
            return view;
        }
    };
}

PS: Note that this solution can misbehave, if you order of child Views changes. Bob's uncle says it's okay for what we need right now. I leave it for future scientists to build upon this.

PPS: If you're working at google, feel free to copy paste it into framework and build on it.

PPPS: You might wanna use better app architecture, like MVVM.