##探究fragment转场动画的执行流程
###我们都知道fragment可以设置它的转场动画,来实现fragment之间的切换的动画效果。如下代码:1
2
3
4
5FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.setCustomAnimations(R.anim.menushow,R.anim.menuhide);
fragmentTransaction.add(R.id.ad_container,new ClsInfoFragment(),"tag1");
fragmentTransaction.commitAllowingStateLoss();
这样简单几句话就可以实现fragment之间的切换效果动画,那其中都经历了什么,我们来跟踪源码看一看流程是怎么走的。
我们先从setCustomAnimations(int enter, int exit)走起,进入源码可见这是一个抽象方法,属于抽象类FragmentTransaction,需要注意的是,此处的exit动画并不适用于出栈动画,如果你试图在这使用它做一个出栈动画是无效的,出栈动画需要用另一个方法
setCustomAnimations(enter, exit, popEnter, popExit),第四个参数才是设置出栈动画
1 | /** |
因为FragmentTransaction是个抽象类,那么我们找到他的实现类BackStackRecord,位于android.support.v4.app包下。它的setCustomAnimations方法只是简单的将我们传入的参数赋值给自身的变量,并返回自身对象
1 | @Override |
那我们再来看下一步fragmentTransaction.add(R.id.ad_container,new ClsInfoFragment(),”tag1”)都做了什么。依然还是BackStackRecord类。
1 | @Override |
BackStackRecord里的add方法里调用了一个doAddOp方法,源码如下:
1 | private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) { |
这里先是将fragment关联到fragmentmanager对象,为fragment设置tag,然后调用一个addOp(new Op(opcmd, fragment))方法,opcmd是上面传入的一个OP_ADD常量,在BackStackRecord中有一系列常量如OP_HIDE
,OP_SHOW
,OP_DETACH
,OP_ATTACH
,OP_ADD
等等来控制下面的流程走向。让我们进入addOp方法:
1 | void addOp(Op op) { |
在这里就将setCustomAnimations方法传入的值给一个Op对象进行赋值并将它添加到一个list里备用。Op是定义在BackStackRecord类里的一个静态final类,BackStackRecord里有一个ArrayList管理这些对应的Op对象。
我们再来看下一步fragmentTransaction.commitAllowingStateLoss()
1 | @Override |
跟踪到commitInternal方法
1 | int commitInternal(boolean allowStateLoss) { |
Fragemntmanager也是个抽象类,它的实现为FragmentManagerImpl类,进入源码跟踪可知
这个方法调用了fragemntmanager的enqueueAction(OpGenerator action, boolean allowStateLoss)方法,它的实现在FragmentManagerImpl 的 enqueueAction方法
1 | /** |
我们可以看到这方法中执行了一个scheduleCommit()方法,继续往下走进去
1 | /** |
我们可以看到这个方法里先remove掉主线程handler里已经存在的退出的动画任务 mExecCommit,
mExecCommit是个ruannable线程任务
1 | Runnable mExecCommit = new Runnable() { |
然后在加入新的退出动画任务
这个任务里执行了execPendingActions()方法,我们继续往下走
1 | /** |
我们可以看到这个方法里先执行了ensureExecReady(true)去执行前置的一些操作,然后在while循环里将挂起操作中的所有东西添加到记录, 以及它们是添加还是 pop 操作 isPop,是否需要操作等。然后进入
removeRedundantOperationsAndExecute(mTmpRecords, mTmpIsPop)方法:
1 |
|
在这方法里,先执行executePostponedTransaction强制的将保存的Transactions除了当前的,其他的清除掉,然后循环取得BackStackRecord的list里的每一个对象来判断是否要执行executeOpsTogether操作
1 | /** |
然后在这个方法里也循环的去取得每一个FragmentTransaction对应操作集的是否是执行出栈分别跳转到对应的方法中执行1
2
3
4 oldPrimaryNav = record.expandOps(mTmpAddedFragments, oldPrimaryNav);
} else {
oldPrimaryNav = record.trackAddedFragmentsInPop(mTmpAddedFragments, oldPrimaryNav);
}
在这些方法中都有大致相同的逻辑,判断Op对象里的对应的状态值进行add,hide,remove等操作
1 | switch (op.cmd) { |
将各种操作封装成Op并添加,最后在executePopOps方法中通过循环给每个fragment调用setNextAnim设置动画属性。如下:
1 | void executePopOps(boolean moveToState) { |
当fragmentmanager在moveToState或者completeShowHideFragment等操作时,都会去调用getNextAnim方法获得动画对象,或者通过loadAnimation方法去间接调用getNextAnim方法获得动画对象,最终在后续操作中完成动画的执行。