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

问题描述 投票:12回答:2

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

我有两个标签,Tab1和Tab2。 Tab1的布局由FragmentA定义,Tab2的布局由FragmentB定义。我按照here给出的方法(编辑:自问题提出以来文档已经改变)。

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

  1. 切换标签时使用hide()/show()而不是attach()/detach()
  2. onPause()中,通过View将当前片段视图状态保存在片段的getView()实例变量中。在onCreateView(Bundle savedInstanceState)中检查此字段是否为非null,如果是这种情况,则返回此字段的值。这个解决方案看起来很糟糕,我被告知它可能也会在我的应用程序中引入内存泄漏。

第二个问题:考虑以下用户交互:用户在Tab1上启动并进行一些更改,使Tab1的视图状态处于与其默认状态不同的状态(我们希望片段通过制表符和设备倾斜来保存此视图状态) 。然后用户转到Tab2。然后用户倾斜她/他的设备(仍然在Tab2)。然后用户切换到Tab1(在新的屏幕方向)。现在,问题是:当用户最初从Tab1切换到Tab2时,片段被分离,从而丢弃其视图(即使片段实例仍然存在)。当用户然后倾斜设备时,活动 - 从而与之相关的FragmentAFragmentB - 被破坏。由于此时FragmentA不再具有视图(记住:它已被分离),因此在调用FragmentA.onSaveInstanceState(Bundle savedInstanceState)期间,我们无法保存其视图元素的状态(例如,启用/禁用了哪些按钮)。在这种情况下如何恢复片段视图状态?唯一可行的解​​决方案是将每个视图元素的不同状态标志保存为SharedPreferences吗?对于这样的“日常工作”来说,这似乎太复杂了。

android-fragments android-tabs
2个回答
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)中。


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) {
}
© www.soinside.com 2019 - 2024. All rights reserved.