如何正确将片段的实例状态保存在后堆栈中? [英] How to correctly save instance state of Fragments in back stack?

查看:85
本文介绍了如何正确将片段的实例状态保存在后堆栈中?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在SO上发现了许多类似问题的实例,但不幸的是,没有一个答案符合我的要求.

我对人像和风景有不同的布局,并且正在使用后退堆栈,这既阻止了我使用setRetainState(),又阻止了使用配置更改例程的技巧.

我在TextViews中向用户显示某些信息,这些信息不会保存在默认处理程序中.当仅使用活动编写我的应用程序时,以下方法运行良好:

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));
}

对于Fragment,这仅在非常特殊的情况下有效.具体来说,令人震惊的是替换一个片段,将其放回堆栈中,然后在显示新片段时旋转屏幕.据我了解,旧片段在被替换时不会收到对onSaveInstanceState()的调用,但仍以某种方式保持与Activity的链接,而当其View不再存在时,此方法将在以后被调用,因此寻找任何我的TextView结果变成了NullPointerException.

此外,我发现对Fragment保留对我的TextViews的引用不是一个好主意,即使对Activity的引用也可以.在那种情况下,onSaveInstanceState()实际上保存了状态,但是如果在隐藏片段时旋转屏幕两次,问题就会再次出现,因为在新实例中不会调用它的onCreateView().

我想到了将onDestroyView()中的状态保存到某些Bundle类型的类成员元素中(实际上是更多数据,而不仅仅是一个TextView),并且将那个保存在onSaveInstanceState()中但是还有其他缺点.首先,如果当前显示片段 ,则调用两个函数的顺序相反,因此我需要考虑两种不同的情况.必须有一个更清洁,正确的解决方案!

解决方案

要正确保存Fragment的实例状态,您应该执行以下操作:

1..在片段中,通过覆盖onSaveInstanceState()保存实例状态并在onActivityCreated()中还原:

class MyFragment extends Fragment {

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ...
        if (savedInstanceState != null) {
            //Restore the fragment's state here
        }
    }
    ...
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        //Save the fragment's state here
    }

}

2.重要点,在活动中,您必须将片段的实例保存在onSaveInstanceState()中,并在onCreate()中还原.

class MyActivity extends Activity {

    private MyFragment 

    public void onCreate(Bundle savedInstanceState) {
        ...
        if (savedInstanceState != null) {
            //Restore the fragment's instance
            mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
            ...
        }
        ...
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        //Save the fragment's instance
        getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);
    }

}

希望这会有所帮助.

I have found many instances of a similar question on SO but no answer unfortunately meets my requirements.

I have different layouts for portrait and landscape and I am using back stack, which both prevents me from using setRetainState() and tricks using configuration change routines.

I show certain information to the user in TextViews, which do not get saved in the default handler. When writing my application solely using Activities, the following worked well:

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));
}

With Fragments, this works only in very specific situations. Specifically, what breaks horribly is replacing a fragment, putting it in the back stack and then rotating the screen while the new fragment is shown. From what I understood, the old fragment does not receive a call to onSaveInstanceState() when being replaced but stays somehow linked to the Activity and this method is called later when its View does not exist anymore, so looking for any of my TextViews results into a NullPointerException.

Also, I found that keeping the reference to my TextViews is not a good idea with Fragments, even if it was OK with Activity's. In that case, onSaveInstanceState() actually saves the state but the problem reappears if I rotate the screen twice when the fragment is hidden, as its onCreateView() does not get called in the new instance.

I thought of saving the state in onDestroyView() into some Bundle-type class member element (it's actually more data, not just one TextView) and saving that in onSaveInstanceState() but there are other drawbacks. Primarily, if the fragment is currently shown, the order of calling the two functions is reversed, so I'd need to account for two different situations. There must be a cleaner and correct solution!

解决方案

To correctly save the instance state of Fragment you should do the following:

1. In the fragment, save instance state by overriding onSaveInstanceState() and restore in onActivityCreated():

class MyFragment extends Fragment {

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        ...
        if (savedInstanceState != null) {
            //Restore the fragment's state here
        }
    }
    ...
    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        //Save the fragment's state here
    }

}

2. And important point, in the activity, you have to save the fragment's instance in onSaveInstanceState() and restore in onCreate().

class MyActivity extends Activity {

    private MyFragment 

    public void onCreate(Bundle savedInstanceState) {
        ...
        if (savedInstanceState != null) {
            //Restore the fragment's instance
            mMyFragment = getSupportFragmentManager().getFragment(savedInstanceState, "myFragmentName");
            ...
        }
        ...
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);

        //Save the fragment's instance
        getSupportFragmentManager().putFragment(outState, "myFragmentName", mMyFragment);
    }

}

Hope this helps.

这篇关于如何正确将片段的实例状态保存在后堆栈中?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆