【问题标题】:Why does Android change the value of EditTexts with same id?为什么Android会更改具有相同id的EditTexts的值?
【发布时间】:2013-12-05 16:36:30
【问题描述】:

我有一个包含 LinearLayout 的片段,其中根据某些业务逻辑对不同的项目进行膨胀。在这些项目中包含一个 EditText。当我有多个具有不同内容的项目并且我分离/附加片段时,所有 EditTexts 都会以某种方式获得所有相同的文本。仅当 EditText 在布局文件中有 id 时才会发生这种情况。

为什么会这样?除了删除id还有其他方法可以防止这种情况吗?我想在我的膨胀项目上使用findViewById 来访问视图,而不是容易出错的getChildAt

我在https://github.com/rodja/EditTextValueProblem创建了一个简约示例来演示该问题

【问题讨论】:

标签: android view android-fragments android-edittext


【解决方案1】:

可以通过在 EditTexts 布局定义中设置 android:saveEnabled="false" 来简单地修复它。当然,您需要确保自己保存/恢复内容。所以这是一个不直观的解决方法——但它适用于我的情况。尽管如此,整个事情看起来都像是一个 Android 错误:

Android 布局系统的一个不错的特点是

ID 在整个树中不必是唯一的 [...]

Android documentation 中所述。这使得代码和布局重用变得更加简单,并且被开发人员大量使用。我认为视图的保存/恢复实例状态实现使用视图的 ID 作为存储其状态的键,因此它依赖于整个树中的唯一性。什么鬼?

更新

我已将ListView 添加到the example at GitHub,这表明ListView 几乎可以肯定使用类似的解决方法来防止EditTexts 遇到此问题。可以看出,在 ListView 内的 EditText 中输入的文本不会自动恢复。

【讨论】:

  • 另外值得注意的是,如果您使用相同的 ID 动态创建了多个视图,那么其中一个不会设置“saveEnabled”就足以与其他视图混淆。因此,为所有人设置它很重要。
  • 感谢您的解决方案。我使用 onAttachedToWindow 和 onDetachedFromWindow 来保存 EditText 输入,然后在再次加载视图时将其设置回来。
【解决方案2】:

还有一种可能,只是改变编辑文本的id,例如,

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

或者如果它是某个自定义布局中的 EditText:

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

这样所有的EditTexts都会有不同的id,文本会被正确恢复。

【讨论】:

  • View.generateViewId() 在 API 级别 17 中添加,用于以编程方式创建视图 ID。我相信也有 ViewCompat 版本。
  • @drdaanger 是的,看起来更优雅。谢谢!
【解决方案3】:

来自未来的问候。可悲的是,Android 框架开发人员仍然喜欢嘲笑我们付出的代价。感谢我们聪明的科学家,我们提出了一个更好的解决方案。见下文(是的,我们将来仍然使用 java)。你可以找到原始的“研究论文”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:请注意,如果您更改子 Views 的顺序,此解决方案可能会出现问题。鲍勃的叔叔说我们现在需要的东西没问题。我把它留给未来的科学家在此基础上继续发展。

PPS:如果您在 google 工作,请随意将其复制粘贴到框架中并在其上构建。

PPPS:您可能想要使用更好的应用架构,例如 MVVM。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-01-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-06-16
    相关资源
    最近更新 更多