免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 1762 | 回复: 1
打印 上一主题 下一主题

[Android] FragmentTransaction add 和 replace 区别 [复制链接]

论坛徽章:
80
20周年集字徽章-庆
日期:2020-10-28 14:09:1215-16赛季CBA联赛之北京
日期:2020-10-28 13:32:5315-16赛季CBA联赛之北控
日期:2020-10-28 13:32:4815-16赛季CBA联赛之天津
日期:2020-10-28 13:13:35黑曼巴
日期:2020-10-28 12:29:1520周年集字徽章-周	
日期:2020-10-31 15:10:0720周年集字徽章-20	
日期:2020-10-31 15:10:07ChinaUnix元老
日期:2015-09-29 11:56:3020周年集字徽章-年
日期:2020-10-28 14:14:56
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2015-09-28 10:44 |只看该作者 |倒序浏览
使用 FragmentTransaction 的时候,它提供了这样两个方法,一个 add , 一个 replace ,对这两个方法的区别一直有点疑惑。我觉得使用 add 的话,在按返回键应该是回退到上一个 Fragment,而使用 replace 的话,那个别 replace 的就已经不存在了,所以就不会回退了。但事实不是这样子的。add 和 replace 影响的只是界面,而控制回退的,是事务。

public abstract FragmentTransaction add (int containerViewId, Fragment fragment, String tag)
Add a fragment to the activity state. This fragment may optionally also have its view (if Fragment.onCreateView returns non-null) into a container view of the activity.

add 是把一个fragment添加到一个容器 container 里。

public abstract FragmentTransaction replace (int containerViewId, Fragment fragment, String tag)
Replace an existing fragment that was added to a container. This is essentially the same as calling remove(Fragment) for all currently added fragments that were added with the same containerViewId and then add(int, Fragment, String) with the same arguments given here.

replace 是先remove掉相同id的所有fragment,然后在add当前的这个fragment。

在大部分情况下,这两个的表现基本相同。因为,一般,咱们会使用一个FrameLayout来当容器,而每个Fragment被add 或者 replace 到这个FrameLayout的时候,都是显示在最上层的。所以你看到的界面都是一样的。但是,使用add的情况下,这个FrameLayout其实有2层,多层肯定要比一层的来得浪费,所以还是推荐使用replace。当然有时候还是需要使用add的。比如要实现轮播图的效果,每个轮播图都是一个独立的Fragment,而他的容器FrameLayout需要add多个Fragment,这样他就可以根据提供的逻辑进行轮播了。

而至于返回键的时候,这个跟事务有关,跟使用add还是replace没有任何关系。

论坛徽章:
80
20周年集字徽章-庆
日期:2020-10-28 14:09:1215-16赛季CBA联赛之北京
日期:2020-10-28 13:32:5315-16赛季CBA联赛之北控
日期:2020-10-28 13:32:4815-16赛季CBA联赛之天津
日期:2020-10-28 13:13:35黑曼巴
日期:2020-10-28 12:29:1520周年集字徽章-周	
日期:2020-10-31 15:10:0720周年集字徽章-20	
日期:2020-10-31 15:10:07ChinaUnix元老
日期:2015-09-29 11:56:3020周年集字徽章-年
日期:2020-10-28 14:14:56
2 [报告]
发表于 2015-09-28 10:47 |只看该作者
FragmentManager 是一个抽象类,实现类是 FragmentManagerImpl ,跟 FragmentManager 在同一个类文件里。FragmentTransaction 也是一个抽象类,具体实现是 BackStackRecord 。BackStackRecord 其实是一个封装了一个队列。咱们看 add 方法和 replace 方法。

add 方法和 replace 方法都是把一个操作 OP_XX 放入到队列里,Op 是其内部封装的一个操作的类。在 BackStackRecord 的 run 方法里,每次会从队列的头(mHead)获取一个操作 Op ,如果 Op 操作是 add ,则调用 FragmentManager 的 addFragment() 方法,如果 Op 操作是 replace ,则先调用 FragmentManager 的 removeFragment() 方法,然后再调用 addFragment() 方法。

下面是 add 方法。
  1. public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {
  2.     doAddOp(containerViewId, fragment, tag, OP_ADD);
  3.     return this;
  4. }
复制代码
下面是 replace 方法。
  1. public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {
  2.     if (containerViewId == 0) {
  3.         throw new IllegalArgumentException("Must use non-zero containerViewId");
  4.     }

  5.     doAddOp(containerViewId, fragment, tag, OP_REPLACE);
  6.     return this;
  7. }
复制代码
add 和 replace 方法都是调用的 doAddOp 方法。也就是把一个操作 Op 添加到队列。
  1. private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {
  2.     fragment.mFragmentManager = mManager;

  3.     if (tag != null) {
  4.         if (fragment.mTag != null && !tag.equals(fragment.mTag)) {
  5.             throw new IllegalStateException("Can't change tag of fragment "
  6.                     + fragment + ": was " + fragment.mTag
  7.                     + " now " + tag);
  8.         }
  9.         fragment.mTag = tag;
  10.     }

  11.     if (containerViewId != 0) {
  12.         if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {
  13.             throw new IllegalStateException("Can't change container ID of fragment "
  14.                     + fragment + ": was " + fragment.mFragmentId
  15.                     + " now " + containerViewId);
  16.         }
  17.         fragment.mContainerId = fragment.mFragmentId = containerViewId;
  18.     }

  19.     Op op = new Op();
  20.     op.cmd = opcmd;
  21.     op.fragment = fragment;
  22.     addOp(op);
  23. }
复制代码
run 方法才是真正执行的方法。什么时候执行先不考虑,只需要知道一系列的操作会一次执行,而不是一个操作执行一次。
run 方法有点大,就看一下 while 循环开始和结束的时候,以及 switch case 里 OP_ADD 和 OP_REPLACE 分支就可以了。
  1. public void run() {
  2.     if (FragmentManagerImpl.DEBUG) {
  3.         Log.v(TAG, "Run: " + this);
  4.     }

  5.     if (mAddToBackStack) {
  6.         if (mIndex < 0) {
  7.             throw new IllegalStateException("addToBackStack() called after commit()");
  8.         }
  9.     }

  10.     bumpBackStackNesting(1);

  11.     SparseArray<Fragment> firstOutFragments = new SparseArray<Fragment>();
  12.     SparseArray<Fragment> lastInFragments = new SparseArray<Fragment>();
  13.     calculateFragments(firstOutFragments, lastInFragments);
  14.     beginTransition(firstOutFragments, lastInFragments, false);
  15.     // 获取队列的头
  16.     Op op = mHead;
  17.     while (op != null) {
  18.         switch (op.cmd) {
  19.             case OP_ADD: {
  20.                 Fragment f = op.fragment;
  21.                 f.mNextAnim = op.enterAnim;
  22.                 mManager.addFragment(f, false);//添加
  23.             }
  24.             break;
  25.             case OP_REPLACE: {
  26.                 Fragment f = op.fragment;
  27.                 if (mManager.mAdded != null) {
  28.                     for (int i = 0; i < mManager.mAdded.size(); i++) {
  29.                         Fragment old = mManager.mAdded.get(i);
  30.                         if (FragmentManagerImpl.DEBUG) {
  31.                             Log.v(TAG,
  32.                                     "OP_REPLACE: adding=" + f + " old=" + old);
  33.                         }
  34.                         if (f == null || old.mContainerId == f.mContainerId) {
  35.                             if (old == f) {
  36.                                 op.fragment = f = null;
  37.                             } else {
  38.                                 if (op.removed == null) {
  39.                                     op.removed = new ArrayList<Fragment>();
  40.                                 }
  41.                                 op.removed.add(old);
  42.                                 old.mNextAnim = op.exitAnim;
  43.                                 if (mAddToBackStack) {
  44.                                     old.mBackStackNesting += 1;
  45.                                     if (FragmentManagerImpl.DEBUG) {
  46.                                         Log.v(TAG, "Bump nesting of "
  47.                                                 + old + " to " + old.mBackStackNesting);
  48.                                     }
  49.                                 }
  50.                                 mManager.removeFragment(old, mTransition, mTransitionStyle);//删除
  51.                             }
  52.                         }
  53.                     }
  54.                 }
  55.                 if (f != null) {
  56.                     f.mNextAnim = op.enterAnim;
  57.                     mManager.addFragment(f, false);//添加
  58.                 }
  59.             }
  60.             break;
  61.             case OP_REMOVE: {
  62.                 Fragment f = op.fragment;
  63.                 f.mNextAnim = op.exitAnim;
  64.                 mManager.removeFragment(f, mTransition, mTransitionStyle);
  65.             }
  66.             break;
  67.             case OP_HIDE: {
  68.                 Fragment f = op.fragment;
  69.                 f.mNextAnim = op.exitAnim;
  70.                 mManager.hideFragment(f, mTransition, mTransitionStyle);
  71.             }
  72.             break;
  73.             case OP_SHOW: {
  74.                 Fragment f = op.fragment;
  75.                 f.mNextAnim = op.enterAnim;
  76.                 mManager.showFragment(f, mTransition, mTransitionStyle);
  77.             }
  78.             break;
  79.             case OP_DETACH: {
  80.                 Fragment f = op.fragment;
  81.                 f.mNextAnim = op.exitAnim;
  82.                 mManager.detachFragment(f, mTransition, mTransitionStyle);
  83.             }
  84.             break;
  85.             case OP_ATTACH: {
  86.                 Fragment f = op.fragment;
  87.                 f.mNextAnim = op.enterAnim;
  88.                 mManager.attachFragment(f, mTransition, mTransitionStyle);
  89.             }
  90.             break;
  91.             default: {
  92.                 throw new IllegalArgumentException("Unknown cmd: " + op.cmd);
  93.             }
  94.         }

  95.         op = op.next;//队列的下一个
  96.     }

  97.     mManager.moveToState(mManager.mCurState, mTransition,
  98.             mTransitionStyle, true);

  99.     if (mAddToBackStack) {
  100.         mManager.addBackStackState(this);
  101.     }
  102. }
复制代码
BackStackRecord 的构造器里参数列表里有一个 FragmentManager ,所有 BackStackRecord 其实是有一个 FragmentManager 的引用的,BackStackRecord 可以直接调用 FragmentManager 的 addFragment 方法。
下面是 FragmentManager 的 addFragment() 方法,每次 add 一个 Fragment,Fragment 对象都会被放入到 mAdded 的容器里。
  1. public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {
  2.     boolean show = false;
  3.     ArrayList<Fragment> newMenus = null;
  4.     if (mAdded != null) {
  5.         for (int i=0; i<mAdded.size(); i++) {
  6.             Fragment f = mAdded.get(i);
  7.             if (f != null) {
  8.                 if (f.performCreateOptionsMenu(menu, inflater)) {
  9.                     show = true;
  10.                     if (newMenus == null) {
  11.                         newMenus = new ArrayList<Fragment>();
  12.                     }
  13.                     newMenus.add(f);
  14.                 }
  15.             }
  16.         }
  17.     }
  18.    
  19.     if (mCreatedMenus != null) {
  20.         for (int i=0; i<mCreatedMenus.size(); i++) {
  21.             Fragment f = mCreatedMenus.get(i);
  22.             if (newMenus == null || !newMenus.contains(f)) {
  23.                 f.onDestroyOptionsMenu();
  24.             }
  25.         }
  26.     }
  27.    
  28.     mCreatedMenus = newMenus;
  29.    
  30.     return show;
  31. }
复制代码
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP