如何正确保存片段的实例状态到返回堆栈中?

9 浏览
0 Comments

如何正确保存片段的实例状态到返回堆栈中?

我在SO上找到了许多类似的问题,但不幸的是没有一个答案符合我的要求。\n我对纵向和横向有不同的布局,并且我正在使用返回堆栈,这两者都使我无法使用setRetainState()和使用配置更改例程的技巧。\n我在TextView中向用户显示特定信息,这些信息在默认处理程序中不会被保存。当我仅使用Activities编写应用程序时,以下方法效果很好:\n

TextView vstup;
@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.whatever);
    vstup = (TextView)findViewById(R.id.whatever);
    /* (...) */
}
@Override
public void onSaveInstanceState(Bundle state) {
    super.onSaveInstanceState(state);
    state.putCharSequence(App.VSTUP, vstup.getText());
}
@Override
public void onRestoreInstanceState(Bundle state) {
    super.onRestoreInstanceState(state);
    vstup.setText(state.getCharSequence(App.VSTUP));
}

\n但在Fragment中,这只在非常特殊的情况下有效。具体来说,当替换一个Fragment,将其放入返回堆栈中,然后在显示新Fragment时旋转屏幕时,会出现严重问题。据我了解,当被替换的旧Fragment在后续被调用时不会收到onSaveInstanceState()方法的调用,但它仍然与Activity有某种联系,而这个方法在其View不存在时被调用,因此查找任何我的TextView会导致NullPointerException。\n此外,我发现保留对TextView的引用对于Fragment来说不是一个好主意,即使对于Activity来说是可以的。在这种情况下,onSaveInstanceState()实际上保存了状态,但如果我在Fragment隐藏时旋转屏幕两次,问题会重新出现,因为它的onCreateView()在新实例中不会被调用。\n我考虑在onDestroyView()中将状态保存到某个Bundle类型的类成员元素中(实际上是更多的数据,而不仅仅是一个TextView),并在onSaveInstanceState()中保存它,但还有其他缺点。首先,如果当前显示Fragment,两个函数的调用顺序会颠倒,因此我需要考虑两种不同的情况。肯定有更清晰和正确的解决方案!

0
0 Comments

如何在返回栈中正确保存fragment的实例状态?

在最新的support库中,不再需要讨论这里的任何解决方案。您可以使用FragmentTransaction随意操作Activity的fragment。只需确保您的fragments能够通过id或tag进行标识。

只要您不尝试在每次调用onCreate()时重新创建它们,fragments将自动恢复。相反,您应该检查savedInstanceState是否不为空,并在这种情况下找到对已创建fragments的旧引用。

这里有一个例子:

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

if (savedInstanceState == null) {

myFragment = MyFragment.newInstance();

getSupportFragmentManager()

.beginTransaction()

.add(R.id.my_container, myFragment, MY_FRAGMENT_TAG)

.commit();

} else {

myFragment = (MyFragment) getSupportFragmentManager()

.findFragmentByTag(MY_FRAGMENT_TAG);

}

...

}

然而要注意,当恢复fragment的隐藏状态时,目前存在一个bug。如果您在activity中隐藏了fragments,则在这种情况下需要手动恢复此状态。

这个修复是您在使用support库时注意到的吗?还是在其他地方读到的?您能否提供更多相关信息?谢谢!

可以从文档中隐含地推断出这一修复。例如,beginTransaction()文档中写道:"This is because the framework takes care of saving your current fragments in the state (...)"。我也一直按照这种预期的行为编写我的应用程序。

如果使用ViewPager是否适用?

通常是的,除非您在FragmentPagerAdapterFragmentStatePagerAdapter的实现中更改了默认行为。例如,如果您查看FragmentStatePagerAdapter的代码,您将看到restoreState()方法会从您在创建适配器时传递的FragmentManager恢复fragments。

您能指定以上提到的版本是哪个吗?

我认为这个回答是对原始问题最好的回答。我认为这个回答最符合Android平台的工作方式。我建议将回答标记为"已接受",以便更好地帮助未来的读者。

我同意这个回答应该是正确的答案。但我需要补充一点,为了使其工作,您需要不重写activity的onSaveInstanceState方法或在您的实现中调用super.onSaveInstanceState。

newInstance()是什么?这个例子不起作用。

0
0 Comments

在处理Fragments实例状态保存的过程中,可能会遇到一些问题。下面是一种复杂但有效的解决方法。首先,在onCreateView()方法中判断savedInstanceState是否为空,如果不为空,则将savedInstanceState中保存的状态恢复到savedState中。然后,如果savedState不为空,则将其中保存的状态设置给相应的View。接着,在onDestroyView()方法中,调用saveState()方法保存当前的状态到savedState中。最后,在onSaveInstanceState()方法中,判断savedState是否为空,如果不为空,则将savedState保存到outState中,否则调用saveState()方法保存当前的状态到outState中。

另外,还有一种解决方法是将要显示的数据保存在变量中,通过View来显示数据,保持两者的同步。

这是目前找到的最好的解决方法,但仍然存在一个问题:如果有两个Fragments A和B,其中A当前在回退栈中,B可见,并且旋转屏幕两次,那么A的状态将会丢失。这是因为在这种情况下只会调用onCreate()方法,而不会调用onCreateView()方法。因此,需要在onCreate()方法中保存状态并在onSaveInstanceState()方法中进行恢复。

希望这个回答对你有帮助!

0
0 Comments

如何正确保存Fragment的实例状态以及解决方法

为了正确保存Fragment的实例状态,你应该按照以下步骤进行操作:

1. 在Fragment中,通过重写onSaveInstanceState()方法保存实例状态,并在onActivityCreated()方法中恢复:

class MyFragment extends Fragment {
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ...
        if (savedInstanceState != null) {
            //在这里恢复Fragment的状态
        }
    }
    ...
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //在这里保存Fragment的状态
    }
}

2. 这是一个重要的点,在Activity中,你需要在onSaveInstanceState()方法中保存Fragment的实例,并在onCreate()方法中恢复:

class MyActivity extends Activity {
    private MyFragment mMyFragment;
    public void onCreate(Bundle savedInstanceState) {
        ...
        if (savedInstanceState != null) {
            //恢复Fragment的实例
            mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
            ...
        }
        ...
    }
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        //保存Fragment的实例
        getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);
    }
}

这对我来说非常完美!没有变通方法,没有黑客技巧,这样做很合理。谢谢你,让我搜索了几个小时才成功。在Fragment中保存值的SaveInstanceState(),然后在Activity中保存Fragment,然后恢复 🙂

mContent是一个Fragment,它是指向Activity中当前Fragment实例的引用。

如果你有可能存在的Fragment(被创建、销毁或者被用户操作替换的Fragment),你可以在Activity的onCreate()方法中使用if(savedInstanceState != null)做如下操作:

if(savedInstanceState.containsKey(FRAG_TAG))){
    frag1 = (FragOne)getFragmentManager().getFragment(savedInstanceState, FRAG_TAG);
}

也许需要对getFragment()的返回值进行转换,例如:

mContent = (ContentFragment)getSupportFragmentManager().getFragment(savedInstanceState, "mContent");

屏幕旋转后,它不会保存存在于后退栈中的Fragment。

你能解释一下这个答案如何保存后退栈中Fragment的实例状态吗?这就是OP所问的问题。

有人能解释一下这个答案如何与保存后退栈中Fragment的状态相关吗?

这与问题无关,当将Fragment放入后退栈时,不会调用onSaveInstanceState()。

为什么需要在Activity的onSaveInstanceState()和onCreate()中进行操作?在Fragment中调用super不会自动完成吗?

在恢复Fragment实例后,mContent在后续的操作中有用吗?

你在更新outState之后,不应该调用super.onSaveInstanceState(outState)吗?

如果正确创建Fragment,就不需要这样做。当savedInstanceState为空时,应该创建Fragment。否则,你将恢复Fragment,但它将被一个全新的Fragment替换。

非常感谢提到putFragment和getFragment。这些方法还不够有名!

我认为Fragment的变量应该在onCreate()中恢复,而不是在onActivityCreated()中。实际上,有些情况下onActivityCreated()不会被调用(例如当Fragment被恢复但在后退栈中时),但你需要正确恢复变量。

设置setRetainInstance(true)会保存Fragment的实例状态吗?是否需要调用Fragment的onSaveInstanceState()?

为什么要使用Activity的onCreate()而不是onRestoreInstanceState()?

我认为对于保存Fragment中的基本数据,不需要使用#2。请参考链接https://inthecheesefactory.com/blog/fragment-state-saving-best-practices/en

当我这样做时,我得到了java.lang.RuntimeException: Unable to start activity ComponentInfo{my.app/my.app.MyActivity}: java.lang.IllegalStateException: Fragment no longer exists for key myFragmentName: index 0。如何解决这个问题?

为什么是onActivityCreated()而不是onCreate()?

这没有意义。如果应用程序仍在后台运行,则不会调用Activity的onCreate()方法。

mMyFragment是什么?

0