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

后端 未结 3 1404
渐次进展
渐次进展 2020-12-28 16:36

I have a Fragment containing a LinearLayout where different items are inflated depending on some business logic. On of these items contains an EditText. When I have multiple

相关标签:
3条回答
  • 2020-12-28 17:05

    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.

    0 讨论(0)
  • 2020-12-28 17:06

    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.

    0 讨论(0)
  • 2020-12-28 17:11

    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.

    0 讨论(0)
提交回复
热议问题