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

时间:2013-03-16 13:18:36

标签: android-fragments android-tabs

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

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

第一个问题:即使我的视图有ID,但在重新连接片段时(在制表符切换旋转之后),它们的状态也不会完全恢复。特别是:具有ID EditText 确实保存了输入的文本,但保存其启用状态。此外,如果启用或禁用按钮,即使它们具有ID,也不会保存。我找到了两个可能解决此问题的方法:

  1. 切换标签时,请使用hide()/show()代替attach()/detach()
  2. onPause()中,通过View将当前片段视图状态保存在片段的getView()实例变量中。在onCreateView(Bundle savedInstanceState)中检查此字段是否为非空,如果是这种情况,则返回此字段的值。这个解决方案看起来很糟糕,我被告知它可能也会在我的应用程序中引入内存泄漏。
  3. 第二个问题:考虑以下用户互动: 用户在Tab1上启动并进行一些更改,使Tab1的视图状态处于与其默认状态不同的状态(我们希望片段通过制表符和设备倾斜来保存此视图状态)。 然后用户转到Tab2。然后用户倾斜她/他的设备(仍然在Tab2)。 然后用户切换到Tab1(在新的屏幕方向)。 现在,问题是:当用户最初从Tab1切换到Tab2时,片段被分离,从而丢弃其视图(即使片段实例仍然存在)。当用户然后倾斜设备时,活动 - 以及与之关联的FragmentAFragmentB都将被销毁。由于此时FragmentA不再具有视图(请记住:它已被分离),因此在调用{{1}期间,我们无法保存其视图元素的状态(例如,启用/禁用了哪些按钮) }。在这种情况下如何恢复片段视图状态?唯一可行的解​​决方案是将每个视图元素的不同状态标志保存为SharedPreferences吗?对于这样的“日常工作”来说,这似乎太复杂了。

2 个答案:

答案 0 :(得分:7)

问题1:

Android默认情况下不会保存您的视图启用状态。似乎只保存了直接受用户操作(没有附加代码)影响的事物。对于普通视图,no information is saved和TextView,其中EditText是子类,entered text is saved(如果设置了freezesText)。

如果你想要保存任何其他东西,你必须自己动手。 Here是一个问题,其中一些答案显示了如何实现自定义视图状态保存。如果你采用这种方法,你可以坚持使用附加/分离。

问题2:

你是对的,你的视图已被销毁后可以调用Fragment.onSaveInstanceState(Bundle)。但是,这不是您应该保存视图状态的位置。 Android会在分离片段时破坏您的视图之前调用View.onSaveInstanceState()。它会保存此状态,并在您再次附加片段时将其返回给您。这正是当您在没有旋转的情况下在标签之间切换时发生的情况。分离时不调用Fragment.onSaveInstanceState(Bundle)。即使您旋转设备,由于分离而保存的视图状态也将保持不变。如果按照上面的指示实现View.onSaveInstanceState(),即使在Tab1-Tab2-rotate-Tab1场景中,您的视图状态也会被正确保存和恢复。

旁注: 尝试旋转时,文档中的example code似乎有些问题。 TabListener的生命周期与Activity的生命周期相同 - 每次旋转时都会创建一个新生命周期。这意味着每次旋转时它也会丢失对片段的内部引用。添加的片段会自动重新创建,因此TabListener不需要在轮换后尝试创建新实例并添加它。相反,对于内部引用,它应该只是尝试在片段管理器中找到具有适当标记的片段。轮换后它仍然存在。

另一个问题是未保存选定的选项卡,但在示例的底部会注明。您可以将其保存在Activity.onSaveInstanceState(Bundle)中。

答案 1 :(得分: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) {
}