Android Fragment 重叠问题

构建MainActivity导航

Fragment 重叠问题原因

Activity 中的 onSaveInstanceState() 里面有一句super.onSaveInstanceState(outState),Google 对于这句话的解释是 “Always call the superclass so it can save the view hierarchy state”,大概意思是“总是执行这句代码来调用父类去保存视图层的状态”

使用 Fragment 的状态保存,当系统内存不足,Fragment 的宿主 Activity 回收的时候,Fragment 的实例并没有随之被回收。Activity 被系统回收时,会主动调用 onSaveInstance() 方法来保存视图层(View Hierarchy),所以当 Activity 通过导航再次被重建时,之前被实例化过的 Fragment 依然会出现在 Activity 中,此时的 FragmentTransaction 中的相当于又再次 add 了 fragment 进去的,hide()和show()方法对之前保存的fragment已经失效了。综上这些因素导致了多个Fragment重叠在一起。

1. 被恢复的fragment加载到Activity
2. onCreate里又重新new了一个fragment实例,也加到了Activity
综上出现了重叠或重影问题

解决方式

建议使用第1种方法解决

1. 解决方法如下,在进入onCreate函数时,先去判断savedInstanceState是否为null,如果不为null,则表示里面有保存着fragment。则不再重新去add这四个fragment,而是通过Tag从前保存的数据中直接去读取

@Override  
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
fManager = getFragmentManager();
if (savedInstanceState != null) {
allFrg = (AllOfficialAccountFragment) fManager.findFragmentByTag("allFrg");
movieFrg = (MovieOfficialAccountFragment) fManager.findFragmentByTag("movieFrg");
newsFrg = (NewsOfficialAccountFragment) fManager.findFragmentByTag("newsFrg");
otherFrg = (OtherOfficialAccountFragment) fManager.findFragmentByTag("otherFrg");
}
super.onCreate(savedInstanceState);
}

2. 重写onAttachFragment,重新让新的Fragment指向了原本未被销毁的fragment,它就是onAttach方法对应的Fragment对象

https://www.jianshu.com/p/90b51f222455

@Override 
public void onAttachFragment(Fragment fragment) {
if (tab1 == null && fragment instanceof Tab1Fragment) tab1 = fragment;
if (tab2 == null && fragment instanceof Tab2Fragment) tab2 = fragment;
if (tab3 == null && fragment instanceof Tab3Fragment) tab3 = fragment;
if (tab4 == null && fragment instanceof Tab4Fragment) tab4 = fragment;
}

3. Activity 中的 onSaveInstanceState() 里面有一句super.onSaveInstanceState(outState);,Google 对于这句话的解释是 “Always call the superclass so it can save the view hierarchy state”,大概意思是“总是执行这句代码来调用父类去保存视图层的状态”。通过注释掉这句话,这样主 Activity 因为种种原因被回收的时候就不会保存之前的 fragment state,也可以成功解决重叠的问题

protected void onSaveInstanceState(Bundle outState) {
//如果用以下这种做法则不保存状态,再次进来的话会显示默认tab
//super.onSaveInstanceState(outState);
}

测试方式

  • 开发者选项打开“不保留活动”
  • 旋转屏幕
  • 用AndroidStudio工具 杀掉应用程序,模拟内存不足回收
  • 转跳到其他Activity、打开多任务窗口、使用Home回到主屏幕再返回

综上正确姿势

  1. fragmentManager.beginTransaction().add,依次添加4个Fragment,这4个Fragment的排序顺序,是由下至上重叠添加,就是后添加的覆盖在前一个的上面
  2. 如果执行add(1)后再add(2),但不调用hide(1)
    1. 如果Fragment都带背景色,此时是看2,看不到1,仅仅是因为背景挡住了
    2. 如果Fragment不带背景色,是透明的,可以看到2在1上面,出现重叠
    3. 然后再执行add(1),而且不调用hide(2),实际是1叠加显示在2上面了
    4. 然后不执行add(1),而是执行show(1),而且不调用hide(2),实际不会有任何变化,还是遮挡和重叠现象
    5. 正确的逻辑就是show一个,hide其他的Fragment;
  3. 旋转屏幕或者被回收重启,执行onSaveInstanceState,然后重新调用onCreate
    1. Activity会自动恢复被保存的Fragment,FragmentManager会依次恢复Fragment,被恢复的Fragment会正常显示;
    2. 如果onCreate不使用fragmentManager.findFragmentByTag找到被缓存的Fragment,而是每次都add一个新实例,实际是fragmentManager内部多了一个Fragment,不仅出现重叠(把背景设透明比较好验证),而且实例变多,再出现onSaveInstanceState和onCreate流程,可能出现多次初始化,难易查找的bug
  4. 综上,Fragment正确的add、show、hide的调用姿势是
    1. add时一定要设置不同的tag
    2. onCreate一定要通过fragmentManager.findFragmentByTag查找是否存在实例
      • 如果不存在,new一个实例,执行add
      • 如果存在,show自己,hide其他

Show me the code

参考项目内 android-library MainActivty