一、起源:

先看效果,在linearLayout中添加了4个Fragment,然后点击替换一次确替换了两个Fragment,引发了我的研究兴趣;

第一次启动                                  点击一次                         点击两次                              点击三次

 

代码很简单  activity  onCreate 方法中添加了4个Fragment

FragmentTransaction transaction =manager.beginTransaction();

transaction.add(R.id.content,fragment1,"a");

transaction.add(R.id.content,fragment1_2,"b");

transaction.add(R.id.content,fragment1_3,"c");

transaction.add(R.id.content,fragment1_4,"d");

transaction.commit();

replace 按钮监听事件中添加了如下代码

Fragment2 fragment2_1 =newFragment2();

FragmentTransaction transaction=manager.beginTransaction();

transaction.replace(R.id.content,fragment2_1,"kk");

transaction.commit();

二、探究transaction.replace到底做了什么

探究源码得知FragmentTransaction 对象是在FragmentManagerImpl 类中的beginTransaction()方法中产生的;

@Override

publicFragmentTransaction beginTransaction() {

returnnewBackStackRecord(this);

}

这才发现BackStackRecord产生的对象才是我们真正使用的FragmentTransaction,那BackStackRecord.replace()  方法究竟做了啥,让我们一探究竟;

publicFragmentTransactionreplace(intcontainerViewId, Fragment fragment, String tag) {

doAddOp(containerViewId, fragment, tag,OP_REPLACE);

return this;

}

public FragmentTransactionadd(intcontainerViewId, Fragment fragment, String tag) {

doAddOp(containerViewId, fragment, tag,OP_ADD);

return this;

}

可以看到add和 replace  方法都没有自己去处理而是交给doAddOp处理,doAddOp()简化代码如下

privatevoiddoAddOp(intcontainerViewId, Fragment fragment, String tag,int opcmd){

fragment.mFragmentManager = mManager;

if (tag!=null) {

fragment.mTag = tag;

}

if(containerViewId != 0) {

fragment.mContainerId = fragment.mFragmentId =containerViewId;

}

Op op =new Op();

op.cmd =opcmd;

op.fragment =fragment;

addOp(op);

}

我们发现,add  和replace  方法都是进行了fragment对象的tag、mFragmentId、mContainerId的赋值,mContainerId是容器id,也就是说每一个fragment 都有保留它被添加的容器的id,也就是我们replace传入的R.id,content;

再看看OP

static final class Op {

Op next;

Op prev;

int cmd;

Fragment fragment;

int enterAnim;

int exitAnim;

int popEnterAnim;

int popExitAnim;

ArrayList<Fragment> removed;

}
Op其实就是保存我们处理动作的一个对象,经过一系列追踪发现,最终在BackStackRecord.run()中处理了这个对象;具体的追踪过程可以参考:https://zhuanlan.zhihu.com/p/20660984

处理源码如下:

switch (op.cmd) {

caseOP_ADD: {

Fragment f = op.fragment;

f.mNextAnim = op.enterAnim;

mManager.addFragment(f,false);

} break;

caseOP_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 =newArrayList<Fragment>();

}

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;

好了终于找到replace的真正处理之处,我们精练出关键代码再看看:

switch (op.cmd) {

caseOP_ADD: {

Fragment f = op.fragment;

mManager.addFragment(f,false);

} break;

caseOP_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 (f == null ||old.mContainerId == f.mContainerId) {

if (old== f) {

op.fragment = f =null;

} else {

mManager.removeFragment(old,mTransition,mTransitionStyle);

}

}

}

}

if (f !=null) {

mManager.addFragment(f,false);

}

} break;

可以看到

1、add方法就是调用了fragmentmanager的添加方法;

2、replace 则是先删除fragmentmanager中所有已添加的fragment中,容器id与当前要添加的fragment的容器id相同的fragment;然后再添加当前fragment;

3、由于添加的时候都是在一个LinearLayout 中,那么所有的 fragment的容器Id都是一样的;

得出结论:  replace 会删除LinearLayout中所有fragment  ,然后再添加传入fragment对象;

好,问题来了,最开始的图片点击第一次删除的是fragment1和fragment1_3  ,第二次只删除了fragment1_3,并没有删除全部,这又是为什么;

带着疑问的态度进行了一次调试,在调试中终于找到了原因,问题就在这段代码:

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

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

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

mManager.removeFragment(old,mTransition, mTransitionStyle);

}

}

mManager.mAdded  是一个ArrayList<Fragment>  列表,在遍历的时候调用了mManager.removeFragment方法,而该方法调用了ArrayList的remove方法;

public void removeFragment(Fragmentfragment, int transition, inttransitionStyle) {

mAdded.remove(fragment);

}

也就是说在用for循环遍历ArrayList列表的时候使用了remove;这是开始怀恋我们的Java老师的了,list遍历列表要删除元素我们要用iterator.remove();

For循环遍历过程删除会造成ArrayList.size()不断变小,所以造成删除不完全的问题;你是否也被坑过。。。

笔记建议  Android此处可以将 mManager.mAdded复制一份再遍历,就不会有这个问题了(亲测有效);

ArrayList<Fragment> list=new ArrayList<Fragment>(mManager.mAdded )  ;    
                for (int i=0; i<list.size();i++) {
                          Fragment old = list.get(i);

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

mManager.removeFragment(old,mTransition, mTransitionStyle);

}

}

三、总结

 用于我们常常使用FrameLayout 做容器fragment都掩盖了下面其他Fragment,大部分情况下看不到此类问题,看不到不表示不存在,笔者建议,遇到此类还是手动去调用remove+add方法,一定要使用replace()可以去修改源码,如果你不嫌麻烦的话。。。

FragmentTransaction.replace() 你不知道的坑的更多相关文章

  1. 怎么通过activity里面的一个按钮跳转到另一个fragment(android FragmentTransaction.replace的用法介绍)

    即:android FragmentTransaction.replace的用法介绍 Fragment的生命周期和它的宿主Activity密切相关,几乎和宿主Activity的生命周期一致,他们之间最 ...

  2. 解决fragmentTransaction.replace不能全屏

    今天遇到个问题,使用fragmentTransaction.replace替换后的内容不能全屏.. FragmentManager fragmentManager = getSupportFragme ...

  3. replace的坑

    问题:html中代码段包含了$,在使用replace替换时,$直接被替换了解决:先把文本中的$全部替换成自己定义的标签,最后在还原回去原因:在介绍replace的文档中,$&代表插入匹配的子串 ...

  4. java调用第三方命令,process.waitfor()挂起(你不知道的坑)

    我们常在java中运行第三方程序,如sh.python,java提供一个Runtime.exec()方法,生成一个Process对象.今天在使用这个方法的时候,发现接口半天没有返回数据.查了一下,原来 ...

  5. MySQL 主从同步架构中你不知道的“坑”(2)

    指定同步库情况 1.binlog_format= ROW模式‍ mysql> use testdb; Database changed mysql> show tables; +----- ...

  6. MySQL 主从同步架构中你不知道的“坑”

    以下操作征对指定不同步库 binlog-format=ROW模式 1 查看主从的binlog模式 mysql> show slave status\G ********************* ...

  7. LVS+Keepalived深度理解,阐述你不知道的坑点

    1. LVS简介 1. 什么是LVS? LVS是Linux Virtual Server的简写,意即Linux虚拟服务器,是一个虚拟的服务器集群系统.本项目在1998年5月由章文嵩博士成立,是中国国内 ...

  8. 错误:The method replace(int, Fragment) in the type FragmentTransaction is not applicable for the arguments (int, MyFragment)

    Fragment newfragment =new MyFragment();fragmentTransaction.replace(R.layout.activity_main,newfragmen ...

  9. FragmentTransaction add 和 replace 区别 转

    使用 FragmentTransaction 的时候,它提供了这样两个方法,一个 add , 一个 replace . add 和 replace 影响的只是界面,而控制回退的,是事务. public ...

随机推荐

  1. Visual Studio将Delop之后生成的dll或者wsp复制到指定目录

    用VS开发sharepoint项目的时候,有很多个project,每个project都会生成一个wsp包,如果手工把wsp文件找到,复制出来,拷贝到服务器上,再部署,就有点麻烦. 所以写了个批处理命令 ...

  2. Android基础面试题

    1. 请描述一下Activity 生命周期. 答: 如下图所示.共有七个周期函数,按顺序分别是: onCreate(), onStart(), onRestart(), onResume(), onP ...

  3. 智者当借力而行, 借助Autodesk应用程序商店实现名利双收

    有没有注意到这个"精选应用"菜单?有没有想过这个菜单下的应用是从哪里来的?你的应用也可以出现在这里哦~ 如果你还不知道,Autodesk在几年前就发布了Autodesk应用程序商店 ...

  4. CALayer 易混淆的两个属性 - position和anchorPoint

    1.简单介绍 CALayer有2个非常重要的属性:position和anchorPoint @property CGPoint position;                           ...

  5. 通过终端编译链接运行C文件

    1.创建c文件 touch demo.c 2.编辑c代码 3.编译(预编译.检查语法.编译).链接 3.1.指令:cc  -c  demo.c 正常情况下,会生成一个demo.o的二进制文件(即:目标 ...

  6. Android 第一个程序 及 环境搭配

    一. JDK配置 1.找到jdk安装路径 2.配置环境变量(建议配置在系统变量里面) 1).配置JAVA_HOME变量 2).配置 CLASSPATH 环境变量 CLASSPATH=.;%JAVA_H ...

  7. Facebook开源动画库 POP-POPSpringAnimation运用

    POPSpringAnimation也许是大多数人使用POP的理由 其提供一个类似弹簧一般的动画效果:实例源代码已经上传至gitHub,地址:https://github.com/wujunyang/ ...

  8. Android打开相机和打开相册

    打开相机 /** * 选择相机 */ private void showCamera() { // 跳转到系统照相机 Intent cameraIntent = new Intent(MediaSto ...

  9. BitTorrent Sync - 神奇的文件同步软件,无需服务器让多台电脑互相同步!

    176,487 微博 腾讯 空间 微信 141 49 如今人们对文件备份和同步的需求已经越来越强烈了.像 Dropbox 一样的云存储网盘有很多,但它们都有一个局限性,就是所有的文件都得经过它们的服务 ...

  10. yii过滤xss代码,防止sql注入

    作者:白狼 出处:www.manks.top/article/yii2_filter_xss_code_or_safe_to_database 本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明 ...