恢复具有相同ID的两个视图的片段

9
我需要实现一个复杂的布局。它有19个部分,可以根据用户先前输入的许多参数来显示或隐藏。 为了简化代码并且不显示未使用的部分,该布局是动态生成的。
所有内容都在一个片段内。 该片段有一个线性布局用作容器,并且在创建该片段时,我会生成所有必要的部分。
每个部分由其自己的本地适配器管理,该适配器负责填充此部分的布局并将其添加到容器中。
一切都运行得非常完美。问题在于有2个部分具有相同的结构,因此它们共享相同的XML布局。由此造成的问题是两个部分的内部视图具有相同的ID。当我进入下一个片段然后返回到这个片段时,系统会尝试恢复视图的先前状态,而当第二个部分被恢复时,它的值也被设置为第一个部分的值,这是一个问题。
是否有任何解决方法可以处理这个问题或者告诉片段不要恢复其状态(因为任何情况下都会手动重新加载)。
以下是当前结构的一个轻量级示例:
片段 XML
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/container"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>
部分 XML
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/section_text"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"/>

片段代码

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    View view = inflater.inflate(R.layout.fragment_layout, container, false);

    if (<condition>)
       createSection1(getContext(),view);

    if (<condition>)        
       createSection2(getContext(),view);

    return view;
}


private void createSection1(Context context, ViewGroup root){
    Section1Adapter adapter = new Section1Adapter(context, root);
    // ...
}

private void createSection2(Context context, ViewGroup root){
    Section2Adapter adapter = new Section2Adapter(context, root);
    // ...
}

适配器代码(两者的思路相同)

public Section2Adapter(LayoutInflater inflater, ViewGroup root) {

    View view = LayoutInflater.from(context).inflate(R.layout.section_layout, root, false);

    initView(view);

    root.addView(view);
}
2个回答

13

你所遇到的问题,正如你所说,基本上是这样的:

enter image description here

你需要做的是告诉 Android 在 SparseArray 中保存哪个 EditText 的状态是哪个键。基本上,你需要达到这个目的:

enter image description here

实现这个机制的方法在 Pasha Dudka 写的 这篇精彩的文章 中解释得非常详细。 (图片由他提供)

只需在该文章中搜索“View IDs should be unique”,就能找到答案。

对于你的特定情况,解决方案的要点有:

  • 你可以继承 LinearLayout,使你的 CustomLinearLayout 知道他的子元素属于哪个 Section ,当它们的状态改变时,你可以将该 section 中所有子元素的状态保存到专为该 section 设计的 SparseArray 中,并将该专用 SparseArray 添加到全局的 SparseArray 中(就像是图片中的那样)。
  • 你可以继承 EditText,使你的 CustomEditText 知道它属于哪个 section,并将它的状态保存在 SparseArray 的自定义键上,例如第一部分的键为section_text_Section1 ,第二部分的键为 section_text_Section2

就我个人而言,我更喜欢第一种版本,因为即使您以后向部分添加更多视图,它也可以正常工作。第二种无法处理更多的视图,因为在第二种情况下,不是父元素进行智能状态保存,而是视图本身。

希望这可以帮到您。


2
哇哦!太棒了!非常感谢,因为我不知道这个。这正是我在寻找的。 - Eselfar
1
我只想说这个东西真的真的为我节省了很多时间和苦楚。非常感谢你! - Sharp
5
链接已经失效。可以在这里找到存档版本。 - l33t
这是Android的错误设计,因为他们激励使用ViewBinding和带有布局的自定义视图。为每个容器创建自定义视图是很麻烦且不正确的做法。 - htafoya
这是安卓的一个错误设计 [...]。 - cjurjiu

1

被接受的解决方案解释得非常好,但有时创建自定义容器类似乎不合适。

一个简单的解决方案是通过编程方式分配ID

ID不是不可变的,因此您可以在视图创建期间更改ID。

假设您填充了一些视图my_custom_view.xml,其中包含一些名为nameEditTextEditText。在视图创建时,您可以使用以下代码(使用视图绑定):

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

这样,保存状态将基于给定的ID存储值。视图创建后将发生状态恢复。

您可以在资源文件中创建自己的ID以实现此目的:

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

</resources>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接