ragment == null) {
tab1Fragment = new Tab1Fragment();
}
tab2Fragment = getFragmentManager().findFragmentByTag(Tab2Fragment.TAG);
if (tab2Fragment == null) {
tab2Fragment = new Tab2Fragment();
}
}
然后点击两个按钮来切换它们, replace(), 并且不加入到back stack中:
@OnClick(R.id.tab1)
void onTab1Clicked() {
getFragmentManager().beginTransaction()
.replace(R.id.content_container, tab1Fragment, Tab1Fragment.TAG)
.commit();
}
@OnClick(R.id.tab2)
void onTab2Clicked() {
getFragmentManager().beginTransaction()
.replace(R.id.content_container, tab2Fragment, Tab2Fragment.TAG)
.commit();
}
可以看到, 每一次的切换, 都是一个Fragment的完全destroy, detach和另一个fragment的attach, create,
但是当我在这两个fragment中各自加上EditText, 发现只要EditText有id, 切换过程中EditText的内容是被保存的.
这是谁在什么时候保存并恢复的呢?
我在TextChange的回调里打了断点, 发现调用栈如下:
在FragmentManagerImpl
中, moveToState()
方法的case Fragment.CREATED中:
调用了: f.restoreViewState(f.mSavedFragmentState);
此时我没有做任何保存状态的处理, 但是断点中可以看出:
虽然mSavedFragmentState是null, 但是mSavedViewState却有值.
所以这个View状态保存和恢复对应的入口即是上面两个图中的入口三.
这是因为我的两个fragment只new了一次, 然后保存了成员变量, 即便是Fragment重新onCreate(), 但是对应的实例仍然是同一个.
这和Activity是不同的, 因为你是无法new一个Activity的.
在上面的例子中, 如果不保存Fragment的引用, 每次都new Fragment, 那么View的状态是不会被保存的, 因为不同实例间的状态传递只有在系统销毁恢复的情况下才会发生(入口一).
如果我们需要在不同的实例间传递状态, 就需要用到下面的方法.
不同Fragment实例间的状态保存和恢复
这里对应的是入口2, 不同于入口1和3, 它们是自动的, 入口2是用户主动保存和恢复的情形.
自己主动保存Fragment的状态, 可以调用FragmentManager的这个方法:
public abstract Fragment.SavedState saveFragmentInstanceState(Fragment f);
它的实现是这样的:
@Override
public Fragment.SavedState saveFragmentInstanceState(Fragment fragment) {
if (fragment.mIndex < 0) {
throwException(new IllegalStateException("Fragment " + fragment
+ " is not currently in the FragmentManager"));
}
if (fragment.mState > Fragment.INITIALIZING) {
Bundle result = saveFragmentBasicState(fragment);
return result != null ? new Fragment.SavedState(result) : null;
}
return null;
}
返回的数据类型是: Fragment.SavedState, 这个state可以通过Fragment的这个方法设置给自己:
public void setInitialSavedState(SavedState state) {
if (mIndex >= 0) {
throw new IllegalStateException("Fragment already active");
}
mSavedFragmentState = state != null && state.mState != null
? state.mState : null;
}
但是注意只能在Fragment被加入之前设置, 这是一个初始状态.
利用这两个方法可以更加自由地保存和恢复状态, 而不依赖于Activity.
这样处理以后, 不必保存Fragment的引用, 每次切换的时候虽然都new了新的实例, 但是旧的实例的状态可以设置给新实例.
例子代码:
@State
SparseArray<Fragment.SavedState> savedStateSparseArray = new SparseArray<>();
void onTab1Clicked() {
// save current tab
Fragment tab2Fragment = getSupportFragmentManager().findFragmentByTag(Tab2Fragment.TAG);
if (tab2Fragment != null) {
saveFragmentState(1, tab2Fragment);
}
// restore last state
Tab1Fragment tab1Fragment = new Tab1Fragment();
restoreFragmentState(0, tab1Fragment);
// show new tab
getSupportFragmentManager().beginTransaction()
.replace(R.id.content_container, tab1Fragment, Tab1Fragment.TAG)
.commit();
}
private void saveFragmentState(int index, Fragment fragment) {
Fragment.SavedState savedState = getSupportFragmentManager().saveFragmentInstanceState(fragment);
savedStateSparseArray.put(index, savedState);
}
private void restoreFragmentState(int index, Fragment fragment) {
Fragment.SavedState savedState = savedStateSparseArray.