使用 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没有任何关系。

2015.08.04 更新。

发现这篇博文被搜索得挺多的,上面是分析是在官方文档上的基础上加上一些个人的猜测,为了避免误人子弟,下面从代码实现的角度做了些分析。希望能帮到大家,也烦请大家在转载的同时注明出处,毕竟写这么一篇博文确实很不容易(binkery)。

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 方法。

public FragmentTransaction add(int containerViewId, Fragment fragment, String tag) {

doAddOp(containerViewId, fragment, tag, OP_ADD);

return this;

}

下面是 replace 方法。

public FragmentTransaction replace(int containerViewId, Fragment fragment, String tag) {

if (containerViewId == 0) {

throw new IllegalArgumentException("Must use non-zero containerViewId");

}

doAddOp(containerViewId, fragment, tag, OP_REPLACE);

return this;

}

add 和 replace 方法都是调用的 doAddOp 方法。也就是把一个操作 Op 添加到队列。

private void doAddOp(int containerViewId, Fragment fragment, String tag, int opcmd) {

fragment.mFragmentManager = mManager;

if (tag != null) {

if (fragment.mTag != null && !tag.equals(fragment.mTag)) {

throw new IllegalStateException("Can't change tag of fragment "

+ fragment + ": was " + fragment.mTag

+ " now " + tag);

}

fragment.mTag = tag;

}

if (containerViewId != 0) {

if (fragment.mFragmentId != 0 && fragment.mFragmentId != containerViewId) {

throw new IllegalStateException("Can't change container ID of fragment "

+ fragment + ": was " + fragment.mFragmentId

+ " now " + containerViewId);

}

fragment.mContainerId = fragment.mFragmentId = containerViewId;

}

Op op = new Op();

op.cmd = opcmd;

op.fragment = fragment;

addOp(op);

}

run 方法才是真正执行的方法。什么时候执行先不考虑,只需要知道一系列的操作会一次执行,而不是一个操作执行一次。

run 方法有点大,就看一下 while 循环开始和结束的时候,以及 switch case 里 OP_ADD 和 OP_REPLACE 分支就可以了。

public void run() {

if (FragmentManagerImpl.DEBUG) {

Log.v(TAG, "Run: " + this);

}

if (mAddToBackStack) {

if (mIndex < 0) {

throw new IllegalStateException("addToBackStack() called after commit()");

}

}

bumpBackStackNesting(1);

SparseArray firstOutFragments = new SparseArray();

SparseArray lastInFragments = new SparseArray();

calculateFragments(firstOutFragments, lastInFragments);

beginTransition(firstOutFragments, lastInFragments, false);

// 获取队列的头

Op op = mHead;

while (op != null) {

switch (op.cmd) {

case OP_ADD: {

Fragment f = op.fragment;

f.mNextAnim = op.enterAnim;

mManager.addFragment(f, false);//添加

}

break;

case OP_REPLACE: {

Fragment f = op.fragment;

if (mManager.mAdded != null) {

for (int i = 0; i < mManager.mAdded.size(); i++) {

Fragment old = mManager.mAdded.get(i);

if (FragmentManagerImpl.DEBUG) {

Log.v(TAG,

"OP_REPLACE: adding=" + f + " old=" + old);

}

if (f == null || old.mContainerId == f.mContainerId) {

if (old == f) {

op.fragment = f = null;

} else {

if (op.removed == null) {

op.removed = new ArrayList();

}

op.removed.add(old);

old.mNextAnim = op.exitAnim;

if (mAddToBackStack) {

old.mBackStackNesting += 1;

if (FragmentManagerImpl.DEBUG) {

Log.v(TAG, "Bump nesting of "

+ old + " to " + old.mBackStackNesting);

}

}

mManager.removeFragment(old, mTransition, mTransitionStyle);//删除

}

}

}

}

if (f != null) {

f.mNextAnim = op.enterAnim;

mManager.addFragment(f, false);//添加

}

}

break;

case OP_REMOVE: {

Fragment f = op.fragment;

f.mNextAnim = op.exitAnim;

mManager.removeFragment(f, mTransition, mTransitionStyle);

}

break;

case OP_HIDE: {

Fragment f = op.fragment;

f.mNextAnim = op.exitAnim;

mManager.hideFragment(f, mTransition, mTransitionStyle);

}

break;

case OP_SHOW: {

Fragment f = op.fragment;

f.mNextAnim = op.enterAnim;

mManager.showFragment(f, mTransition, mTransitionStyle);

}

break;

case OP_DETACH: {

Fragment f = op.fragment;

f.mNextAnim = op.exitAnim;

mManager.detachFragment(f, mTransition, mTransitionStyle);

}

break;

case OP_ATTACH: {

Fragment f = op.fragment;

f.mNextAnim = op.enterAnim;

mManager.attachFragment(f, mTransition, mTransitionStyle);

}

break;

default: {

throw new IllegalArgumentException("Unknown cmd: " + op.cmd);

}

}

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

}

mManager.moveToState(mManager.mCurState, mTransition,

mTransitionStyle, true);

if (mAddToBackStack) {

mManager.addBackStackState(this);

}

}

BackStackRecord 的构造器里参数列表里有一个 FragmentManager ,所有 BackStackRecord 其实是有一个 FragmentManager 的引用的,BackStackRecord 可以直接调用 FragmentManager 的 addFragment 方法。

下面是 FragmentManager 的 addFragment() 方法,每次 add 一个 Fragment,Fragment 对象都会被放入到 mAdded 的容器里。

public void addFragment(Fragment fragment, boolean moveToStateNow) {

if (mAdded == null) {

mAdded = new ArrayList();

}

if (DEBUG) Log.v(TAG, "add: " + fragment);

makeActive(fragment);

if (!fragment.mDetached) {

if (mAdded.contains(fragment)) {

throw new IllegalStateException("Fragment already added: " + fragment);

}

mAdded.add(fragment);

fragment.mAdded = true;

fragment.mRemoving = false;

if (fragment.mHasMenu && fragment.mMenuVisible) {

mNeedMenuInvalidate = true;

}

if (moveToStateNow) {

moveToState(fragment);

}

}

}

有时候,咱们 add Fragment A, 然后 add Fragment B,B 把 A 都覆盖了,点击菜单的时候 A 和 B 的菜单选项都出来了,这是为什么?原因在下面。当在创建 OptionsMenu 的时候,FragmentManager 遍历了 mAdded 容器,所以 A 和 B 的菜单都被添加进来了。也就是说使用 add 的方式,虽然 B 把 A 覆盖住了,但是 A 还是存活的,而且是活动着的。

public boolean dispatchCreateOptionsMenu(Menu menu, MenuInflater inflater) {

boolean show = false;

ArrayList newMenus = null;

if (mAdded != null) {

for (int i=0; i

Fragment f = mAdded.get(i);

if (f != null) {

if (f.performCreateOptionsMenu(menu, inflater)) {

show = true;

if (newMenus == null) {

newMenus = new ArrayList();

}

newMenus.add(f);

}

}

}

}

if (mCreatedMenus != null) {

for (int i=0; i

Fragment f = mCreatedMenus.get(i);

if (newMenus == null || !newMenus.contains(f)) {

f.onDestroyOptionsMenu();

}

}

}

mCreatedMenus = newMenus;

return show;

}

小结:fragment中的add和replace方法的区别

使用add方法时,需要考虑fragment引用被清空的情况。

使用add方法add到activity里面的fragment的对象并不会被销毁。也就是它任然在activity中存在,只是应用被置为null而已。此时如果重新为fragment赋值,其hide方法和show方法都不会生效。如果这种情况下,一个activity中有多个fragment,很可能出现多个fragment层叠而不能正常的显示或者隐藏。

使用add方法使用的fragment的优点在于它占用内存资源少,通过replace方法使用fragment占用资源虽然会多一些,但是不存在add方法的bug。

所以开发的时候,尽量处理好add方法可能引起的bug。

fragment还要处理好commit和transaction.commitAllowingStateLoss()两个方法。

以上所述是小编给大家介绍的fragment中的add和replace方法的区别浅析,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对脚本之家网站的支持!

android add fragment,fragment中的add和replace方法的区别浅析相关推荐

  1. jquery中prop()方法和attr()方法的区别浅析

    引用:http://www.jb51.net/article/41170.htm 官方例举的例子感觉和attr()差不多,也不知道有什么区别,既然有了prop()这个新方法,不可能没用吧,那什么时候该 ...

  2. java e.getmessage() null,浅谈Java异常的Exception e中的egetMessage()和toString()方法的区别...

    Exception e中e的getMessage()和toString()方法的区别: 示例代码1: public class TestInfo { private static String str ...

  3. python predict_对Keras中predict()方法和predict_classes()方法的区别说明

    1 predict()方法 当使用predict()方法进行预测时,返回值是数值,表示样本属于每一个类别的概率,我们可以使用numpy.argmax()方法找到样本以最大概率所属的类别作为样本的预测标 ...

  4. java——Scanner中nextLine()方法和next()方法的区别

    遇到一个有意思的东西,在整理字符串这块知识的时候,发现我在用Scanner函数时,在字符串中加入空格,结果空格后面的东西没有输出来(/尴尬),不多说直接上代码: import java.util.Sc ...

  5. android之在view中内嵌浏览器的方法

    我要做的一个东西是在一个页面的中间嵌入浏览器,一开始不知道从哪里开始,因为以前用的都是Textveiw或者editVeiw之类的控件,而它们并不能用来显示网页的内容,怎么办呢? 首先想到的是:是不是有 ...

  6. android代码 IMEI,Android_Android中获取IMEI码的方法,核心代码:Imei = ((TelephonyManager) - phpStudy...

    Android中获取IMEI码的方法 核心代码:Imei = ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)) .getDeviceId ...

  7. Cesium 中两种添加 model 方法的区别

    概述 Cesium 中包含两种添加 model 的方法,分别为: 通过 viewer.entities.add() 函数添加 通过 viewer.scene.primitives.add() 函数添加 ...

  8. ExtJS中listener方法和handler方法的区别

    listener方法和handler方法的区别在文档中的说明的太玄乎了,看不懂 listeners监听能够对一个click Event事件添加任意多个的事件响应处理函数 而handler处理只能够通过 ...

  9. Adapter中notify(),notifyAll(),notifyDataSetChanged(),notifyDataSetInvalidaded()方法的区别

    1.notify()与notifyAll() notify()和notifyAll()一般用于唤醒被锁或等待中的adapter,两者都是object对象用于通知处在等待该对象的线程方法.notify唤 ...

最新文章

  1. java poi jar maven_导出maven项目依赖的jar包(图文教程)
  2. 【2008】奥运门票4月15日开始预定 四种购买方式供选
  3. 三体系建设是什么意思_湖南信息系统建设和服务能力评估体系怎么做?是什么?...
  4. mysql ignore space_MySQL日志存储空间满引发的错误
  5. Eclipse启动项目报启动上下文失败问题解决方案总结
  6. VS2013 产品密钥 – 所有版本
  7. [生活] 2015年终总结,2016开篇计划
  8. 电气工程学计算机有用吗,电气工程及其自动化就业方向 有前途吗
  9. 经典多级时间轮定时器(C语言版)
  10. spring批量写入mysql数据库_spring boot 向数据库写入海量数据
  11. 为什么要求高频pcb板低ε(Dk)
  12. vue3+ts+setup语法糖
  13. 商业计划汇报PPT模板
  14. css3僵尸走路动画js特效
  15. 编程中的Context(上下文)
  16. 前端拓展:如何开发一个 Chrome 插件?
  17. 双向可控硅的四象限触发方式
  18. vue读取json文件
  19. 扛住100亿次请求——如何做一个“有把握”的春晚红包系统
  20. VS2010离线安装包(学习版)

热门文章

  1. 服务器时区修改引发的后果
  2. 新倩女服务器修复,客户端崩溃及虚拟内存设置解决引导
  3. 如何让 Xcode 在运行时问题(表示为紫色的小三角形)被发现时就立即中断以供调试
  4. 全网搜歌音乐播放器Listen1 Mac中文版附chrome版
  5. 21天转型容器实战营(一了解容器的基本知识)
  6. 网上引流怎么加人快?教你如何快速引流!
  7. 严介和在管理创新与企业家成长论坛上的演讲节选
  8. 深度 | 区块链对人工智能的变革:去中心化将带来数据新范式
  9. html中长英文换行问题
  10. 头像 HTML5 JSON PHP 摄像头,canvas之自定义头像功能实现代码示例