文章目录
我们知道Activity的页面组织形式有“任务栈”的概念,它管理着Activity页面的进出规则。类似的,Fragment作为一种轻量级的页面,也有Fragment后台栈的概念。本文将介绍Fragment后台任务栈(BackStack)的相关知识。
1. 任务栈的概念
首先,“栈”是数据结构中一种盛放数据的模型,特点是“先进后出(FILO,First in last out)”。好比是一个瓶子,数据只能从瓶口放入,从瓶口取出,并且每个数据都是盖在前一个之上的。这就意味着,要想取出下面的,就得先把上面的取出来。因此是先进后出。
类比到我们的Android页面,也存在这样一个“页面栈”,每跳转一个新页面,就意味着该页面被放入“页面栈”中,盖着最上面。用户能看到的只有当前栈顶的页面,当按下返回键时,栈顶页面被弹出,下一个页面处于栈顶,呈现在用户面前。
2. Fragment后台栈
从前文中我们已经了解到如何添加Fragment,但并没有把它加入BackStack。当然,并不是一定要加入BackStack,这取决于我们的需求场景。那么是否加入BackStack有什么不同呢?
添加Fragment,未加入BackStack
下面代码是添加Fragment的简单步骤,我们通过fragmentTransaction.add(R.id.fragment_container, fragmentA).commit();
将FragmentA添加到R.id.fragment_container
这个容器上。
// 1.准备FragmentManager 和 FragmentTransaction
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
// 2.创建Fragment
FragmentA fragmentA = new FragmentA();
// 3.添加Fragment (不要忘记最后的commit)
fragmentTransaction.add(R.id.fragment_container, fragmentA).commit();
// 或者:fragmentTransaction.add(R.id.fragment_container, FragmentA.class, null).commit();
同样的方式,我门再添加FragmentB、FragmentC到R.id.fragment_container
这个容器上。此时,该容器上被添加了三个Fragment,那么它们是怎样的存在形式呢?
-
页面“穿透”
直观感觉是,三个Fragment依次覆盖,从下到上为FragmentA、FragmentB、FragmentC,用户当前看到的是FragmentC,似乎和“栈”一样。但这只是表象。的确,三者是这样的关系,但和栈不同的是,三个页面是“穿透的”。也就是用户点击上面的FragmentC的空白处(没有按钮等View),会透传点击到下面的FragmentB。因为它们并不是用“栈”盛放的,而是像普通View一样添加进去而已,类似FrameLayout里添加View,可以想象到吧。 -
返回键退出所有Fragment
虽然被添加到Fragment也是一个盖着一个,但整体来看还是Activity里的一个容器页面,当点返回键的时候,作用到整个Activity上,Activity销毁退出,它上面所有的Fragment也都销毁退出了。
添加Fragment,并加入BackStack
在添加Fragment的时候,我们可以通过addToBackStack("myBackStack")
将其同时加入到后台栈里,“myBackStack”是本次添加对应的栈帧名,可以理解为在栈中的一个标记。如下面代码所示:
getSupportFragmentManager().beginTransaction()
.add(R.id.fragment_container, FragmentA.class, null)
.addToBackStack("myBackStack")
.setReorderingAllowed(true)
.commit();
同样的方式再添加FragmentB、FragmentC到R.id.fragment_container
里。此时它们的存在形式上怎样的呢?
-
页面“穿透”
Fragment也是一个盖一个的添加到容器上,同样有页面点击穿透的现象。 -
返回键退出栈顶Fragment
这就是Fragment后台栈的主要作用之一。Fragment不仅被添加到容器上,还被添加到BackStack里,可以响应返回键,从而依次退出栈顶的Fragment,像Activity的出栈一样。
应用到当前例子就是:按返回键,FragmentC、FragmentB、FragmentA依次退出,再按返回才是退出Activity。
3. BackStack暂存与恢复Fragment(save/restore)
saveBackStack
BackStack的另一个作用是,可以暂存一些Fragment,将其保存在一个看不见的地方。页面表现是这些Fragment被弹出了。
另外,值得注意的一点是,它会弹出指定stack名及其以上的Fragment
FragmentManager fragmentManager = getSupportFragmentManager();
// 弹出 myBackStack及其以上所有的Fragment
fragmentManager.saveBackStack("myBackStack");
上面代码,就是把最下面的栈帧名为“myBackStack”的Fragment及其以上所有的Fragment全部弹出,暂时保存起来了。
这个过程,被弹出(暂存)的Fragment生命周期会发生变化,依次执行生命周期方法----onPause-----onStop------onDestroyView----onDestroy—
restoreBackStack
既然有暂存,就会有对应的恢复。
FragmentManager fragmentManager = getSupportFragmentManager();
// 恢复 myBackStack1及其以上所有的Fragment
fragmentManager.restoreBackStack("myBackStack1");
上面代码,就是把之前弹出(暂存)的那些Fragment又重新添加,并加入BackStack,栈帧的名字也和之前一样。
4. Remove方法不会将BackStack里的Fragment移除
前文我们介绍了Fragment的增删替查等操作。需要注意的一点是:对于加入到BackStack里的Fragment,只有通过返回键或者saveBackStack方法才能移除。普通的remove操作,只是把Fragment从容器View中移除,BackStack里仍然有该Fragment。
也就意味着,假如你已经调用remove方法移除了当前Fragment,页面表现确实是该页面消失了。但此时你按返回键,BackStack里的Fragment仍能得到响应而退出,而Activity没有得到响应,给你的感觉是,页面什么也没变化。