我们很多应用都涉及到fragment的创建和管理,比如:下面n个tab,上面n个fragment的组合,activity管理多个fragment或者一个fragment等等。
很多应用在MainActivity里的onCreate里去 一 一 实例化fragment,然后在tab切换的时候使用FragmentTransaction去添加或者替换fragment。
大概是这么写的1
2
3
4
5
6
7
8
9
10@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.e("StateActivity", "---------------onCreate");
setContentView(R.layout.activity_main_for_state);
FragmentManager fm = getSupportFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.replace(R.id.state_fragment_container, Fragment1.newInstance("", ""));
ft.commit();
}
这种有没有问题呢?看着好像没有问题,但是如果你打开不保存状态然后再调试或者打印log你就会会发现问题了。
我们有A,B两个Activity,A里有一个fragment,fragment里有一个按钮,可以跳转到Activity B。
我们打开不保存状态,点击fragment里的按钮跳转到B,然后返回,看看A以及fragment的哪些生命周期调用了
看看log的打印信息:
(test是在onSavedInstanceState中保存的字符串)
我们可以看到:
1. Fragment被创建两次。
2. 第二次创建的fragment获取不到我们保存的状态。
想一想也是有道理的:
在不保存状态的情况下,跳转到B之后,A和A里面的fragment都被销毁了,回到A的时候,log中打印的第一个fragment是系统保存状态然后恢复的fragment,所以保存的状态都能获取到,但是第二个fragment是在恢复activity运行生命周期onCreate方法的时候我们代码new出来的,所以第二个fragment里面是没有我们之前保存的状态的。
这种问题要怎么解决呢?
如果只是解决问题1,重复创建fragment的情况(不想有两个fragment实例,因为有的如果是透明fragment可能还会出现重叠),很好解决使用replace,或者不调用fragment的super.onSavedInsance()等等。
但是如果我们想要在fragment中保存状态要怎么办呢?
1如果是一个fragment的activity。
我们可以先从fragmentManager里去取,如果取到了,那么说明这个fragment已经有了,那么我们就什么也不做,如果没取到,我们再去创建。
1 | @Override |
再来运行一下看看log:
Fragment只被创建了一次,并且也能获取到他所保存的状态。
(其实也可以根据savedInstanceState是否为空来判断是否需要重新创建fragment。)
如果一个activity里需要创建多个fragment并且以tab的方式来切换,我们应该怎么做?
借鉴fragmentTabHost里的做法,用一个TabInfo实体类来保存对应的tab和fragment之间的关系。对于fragment的创建,还是先从fragmentManager里去取,如果没有再去创建。
使用一个TabInfo实体类来保存fragment和每个tab之间的关系。
比如:1
2
3
4
5
6
7
8
9
10
11public class TabInfo {
public final String tag;
public final Class<? extends Fragment> clazz;
public final int viewId;
public TabInfo(String tag, Class<? extends Fragment> clazz, int viewId) {
this.tag = tag;
this.clazz = clazz;
this.viewId = viewId;
}
}
然后用一个list来保存所有的tab信息:1
2
3
4private ArrayList<TabInfo> tabInfos = new ArrayList<TabInfo>(2);
//在onCreate()中初始化tab
tabInfos.add(new TabInfo(Fragment1.class.getSimpleName(), Fragment1.class, R.id.tab_right));
tabInfos.add(new TabInfo(Fragment2.class.getSimpleName(), Fragment2.class, R.id.tab_left));
当然这里做的比较粗糙,使用两个按钮做tab,也可以使用其他的自定义view。
切换的时候1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17private void switchTab(int tabIndex) {
TabInfo tabInfo = tabInfos.get(tabIndex);
FragmentManager fm = getSupportFragmentManager();
Fragment f = fm.findFragmentByTag(tabInfo.tag);
FragmentTransaction transaction = fm.beginTransaction();
if (f == null) {
f = Fragment.instantiate(this, tabInfo.clazz.getName());
transaction.add(R.id.tab_fragment_container, f, tabInfo.tag);
}
if (currentFragment != null && currentFragment != f) {
transaction.hide(currentFragment);
}
transaction.show(f);
transaction.commit();
currentFragment = f;
this.tabIndex = tabIndex;
}
这里替换fragment用的是show/hide(不销毁fragment和视图),当然你也可以使用detach/attach(只销毁视图不销毁fragment实例)
http://www.voidcn.com/blog/u013168615/article/p-5794851.html (这篇文章讲show/hide,detach/attach等等比较详细)
再来看看我们的log:
一切正常!