开发者社区> 技术小胖子> 正文
阿里云
为了无法计算的价值
打开APP
阿里云APP内打开

FragmentTransaction.replace() 你不知道的坑

简介:
+关注继续查看
福利推荐:阿里云、腾讯云、华为云等大品牌云产品全线2折优惠活动来袭,4核8G云服务器899元/3年,新老用户共享优惠,点击这里立即抢购>>>

一、起源:

先看效果,在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()可以去修改源码,如果你不嫌麻烦的话。。。



? ? 本文转自 一点点征服 ? 博客园博客,原文链接:http://www.cnblogs.com/ldq2016/p/6245968.html,如需转载请自行联系原作者


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
PHP 7.0.0中ereg_replace 函数使用preg_replace替换方法
PHP 7.0.0中ereg_replace 函数使用preg_replace替换方法
128 0
mysql:insert ignore、insert和replace区别
mysql:insert ignore、insert和replace区别
60 0
在Eclipse中创建Django项目
在以前的分享中,我们是在命令行模式下创建Django项目的,那么,如何在IDE中使用Django呢? 本文将介绍如何在Eclipse中创建Django项目。
920 0
《软件需求工程(第2版)》一1.6 其他一些基本概念
本节书摘来自华章出版社《软件需求工程(第2版)》一书中的第1章,第1.6节,作者 毋国庆 梁正平 袁梦霆 李勇华,更多章节内容可以访问云栖社区“华章计算机”公众号查看
975 0
.replace(/-/g,"/")的用法
  /-/g正则表达式   g  代表  global    全部替换  var str1 ="2012-08-12 23:13"; str1 = str1.replace(/-/g,"/"); var date = new Date(str1 ); alert(date.
1229 0
PLSQL_基础系列12_替换函数用法REPLACE / TRANSLATE / REGEXP_REPLACE
20150806 Created By BaoXinjian 一、摘要 1. Translate (1). 语法:TRANSLATE(char, from, to) (2). 用法: 返回将出现在from中的每个字符替换为to中的相应字符以后的字符串。
1116 0
MySQL replace into (insert into 的增强版)
在使用SQL语句进行数据表插入insert操作时,如果表中定义了主键,插入具有相同主键的记录会报错:    Error Code: 1062. Duplicate entry 'XXXXX' for key 'PRIMARY'(主键冲突)   这样我们只好检查主键是不是存在,若存在则更新,若不存在则插入。
704 0
bboss持久层事务管理组件TransactionManager增加两个release方法
bboss持久层事务管理组件TransactionManager增加release和releasenolog两个方法,可以在finally块中调用它们来释放事务资源,使得bboss持久层框架的编程事务管理变得更加优雅、更加轻松。
638 0
文章
问答
文章排行榜
最热
最新
相关电子书
更多
低代码开发师(初级)实战教程
立即下载
阿里巴巴DevOps 最佳实践手册
立即下载
冬季实战营第三期:MySQL数据库进阶实战
立即下载


http://www.vxiaotou.com