安卓导航标签:恢复片段视图状态

12

我正在试图理解如何在使用导航选项卡时保留片段视图状态。在我的尝试中,我遇到了两个问题,没有找到任何合适的解决方案。

我有两个标签,Tab1和Tab2。 Tab1的布局由FragmentA定义,而Tab2的布局由FragmentB定义。 我遵循这里给出的方法(编辑:文档自此提问以来已更改)。

第一个问题:即使我的视图具有ID,在重新附加片段(在选项卡切换旋转后)时它们的状态也无法完全恢复。特别是:具有ID的EditText确实保存其输入的文本,但未保存其启用状态,按钮也不保存它们的启用或禁用状态,尽管它们具有ID。 我已经找到了两个可能的解决方法:

  1. 在切换选项卡时使用hide()/show()而不是attach()/detach()
  2. onPause()中,通过getView()将当前片段视图状态保存在片段的View实例变量中。在onCreateView(Bundle savedInstanceState)中,检查此字段是否非空,如果是,则返回此字段的值。这个解决方案似乎有些hacky,并且我被告知它可能会在我的应用程序中引入内存泄漏。

第二个问题:考虑以下用户交互: 用户开始在Tab1上并进行一些更改,将Tab1的视图状态置于不同于其默认状态的状态(我们希望片段通过选项卡切换和设备旋转保存此视图状态)。 然后用户进入Tab2。用户然后倾斜她/他的设备(仍在Tab2上)。 然后用户切换到Tab1(在新的屏幕方向下)。

现在的问题是:当用户最初从Tab1切换到Tab2时,片段会被分离,其视图也会被丢弃(尽管片段实例仍然存在)。当用户倾斜设备时,活动 - 以及与之关联的FragmentAFragmentB - 都会被销毁。由于此时FragmentA已经没有视图(请记住:它已经被分离),因此我们无法在调用FragmentA.onSaveInstanceState(Bundle savedInstanceState)期间保存其视图元素的状态(例如,哪些按钮启用/禁用)。在这种情况下如何恢复片段视图状态?难道唯一可行的解决方案是将每个视图元素的不同状态标志保存为SharedPreferences吗?这似乎对于这样一个“日常工作”来说过于复杂。
2个回答

8

问题1:

默认情况下,Android不会保存您的视图启用状态。似乎只有由用户操作直接影响的事物(没有其他代码)才会被保存。对于普通视图 ,不保存任何信息,而对于TextView,其中EditText是它的子类,将输入的文本保存了 (如果设置freezesText)。

如果您想要保存其他内容,则必须自己实现。在这里有一个问题,其中一些答案显示了如何实现自定义视图状态保存,如果您遵循该方法,您可以坚持附加/分离方法。

这里 是一个问题,其中一些答案显示了如何实现自定义视图状态保存,如果您遵循该方法,您可以坚持附加/分离方法。

问题2:

您是正确的,在Fragment.onSaveInstanceState(Bundle)在销毁视图后可能会被调用。但是,这不是您应该保存视图状态的地方。当移除片段时,Android将在销毁视图之前立即调用View.onSaveInstanceState()。它保存此状态并在您重新附加片段时将其返回给您。这正是在没有旋转的情况下在选项卡之间翻转时发生的情况。在分离时不会调用Fragment.onSaveInstanceState(Bundle)。即使您旋转设备,由于分离而保存的视图状态也将持续存在。如果按照上述指示实现View.onSaveInstanceState(),则您的视图状态将被正确保存和恢复,即使是在Tab1-Tab2-rotate-Tab1场景下。

附注:

文档中的示例代码在尝试旋转时似乎存在一些问题。 TabListener的生命周期与Activity相同 - 每次旋转都会创建一个新的TabListener。这意味着每次旋转时,它也会失去对片段的内部引用。添加的片段会自动重新创建,因此在旋转后,TabListener不需要尝试创建新实例并将其添加。相反,在内部引用之外,它应该尝试在片段管理器中查找具有适当标记的片段。旋转后它仍将存在。

另一个问题是选定的选项卡没有保存,但是这在示例底部有说明。您可以在Activity.onSaveInstanceState(Bundle)中保存它。


非常感谢 - 解释得很好。然而,对我来说仍然不是完全清楚的。我已经查看了你所提到的答案,但我没有理解它的用途。什么被认为是“CustomView”?我的片段视图状态仅由内置视图组成(如EditTexts、Buttons等)。因此,我没有扩展任何视图类 - 我只是在xml布局文件中构建了片段视图。如何将答案中提供的示例代码与我的布局连接起来,而我的布局不使用任何用户定义的视图(假设CustomView对于Android来说就像用户定义的控件对于ASP .NET一样)? - Janus Varmarken
1
@jvmk 要使用View.onSaveInstanceState(),您需要子类化一个View并覆盖该方法。然后,您的布局XML需要引用这些新视图,而不是EditText/Button。 - antonyt
2
哇,这太疯狂了。我以为恢复内置视图的视图状态是一个经常发生的任务,应该支持作为标准功能。我想我应该写一个库以备将来使用。感谢您在这里帮助我。这已经让我烦恼了两个星期了,呵呵。 - Janus Varmarken

0
private ViewPager viewPager;
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {

        @Override
        public void onPageSelected(int position) {
            // on changing the page
            // make respected tab selected
            actionBar.setSelectedNavigationItem(position);
        }

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) {
        }

        @Override
        public void onPageScrollStateChanged(int arg0) {
        }
    });
}

@Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}

@Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
    // on tab selected
    // show respected fragment view
    viewPager.setCurrentItem(tab.getPosition());
}

@Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}

2
请考虑在您的答案中包含一些信息,而不仅仅是发布代码。我们试图提供的不仅仅是“修复”,而是帮助人们学习。您应该解释原始代码中出了什么问题,您做了什么不同的事情以及为什么您的更改有效。 - Andrew Barber

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