再谈Fragment
Fragment 就是一个 Activity 布局的一部分,可以把 Activity 的一部分布局抽离出来到 Fragment 中,并且 Fragment 也可以执行逻辑。就是把 Activity 复杂的内容抽离成几个碎片,然后拼凑起来,在 Activity 布局中,只需要引入各个不同的 Fragment 就可以 了。
比如我们常见的布局,底部导航,然后每个模块对应不同的 Fragment。其实也是完全可以不使用 Fragment 的,一个页面中包含各个模块的布局,然后通过点击导航来决定隐藏哪些模块。这样写的话一个 Activity 中的代码逻辑会非常多,所有模块都融合到一个 Activity 中了,相当冗余,耦合。如果用 Fragment 就灵活多了,Activity 只需要根据导航显示对应的 Fragment 就可以了。
如果别的地方需要某一个模块,直接拿出对应的 Fragment 就可以了。如果都写在 Activity 中那抽离出来就费劲了,也增加了不确定性。
再比如一个例子:网易新闻 手机端 和平板端。
平板端是新闻的标题和详情都在一个页面中。手机是详情在单独的一个页面。这样我们就可以将 标题 和 详情写成两个 Fragment。充分利用了。
Fragment 相关的三个类
-
Fragment
具体的 Fragment -
FragmentManager
是管理 Fragment 的 -
FragmentTransaction
通过事务来进行添加 Fragment、隐藏、移除 等操作 Fragment 动作,事务保证了原子性
主要方法
-
transaction.add()
向 Activity 中添加 Fragment,只是添加 Fragment,不影响之前 Fragment 的声明周期,这一点和 Activity 不一样,Activity 会因为新 Activity 而走生命周期。而.add
不会。假如 FragmentA 已经在 Activity 中了,这个时候启动了 FragmentB,A 的生命周期是不会有任何变化的,还是处于onResume
,并不会因为B
处于onResume
而有影响。 -
transaction.remove()
从 Activity 中移除一个Fragment
。如果这个 Fragment 没有被添加到回退栈中,则实例被销毁。如果添加到回退栈了,会执行onDestroyView
实例并不会被销毁。 -
transaction.replace()
使用 Fragment 替换当前,相当于先移除之前的 Fragment(remove()) 然后再 add() -
transaction.hide()
隐藏当前 Fragment。仅仅是设为不可见。不会销毁 -
transaction.show()
显示之前隐藏的 Fragment -
detach()
会将 view 从 UI 中移除。和 remove 不同的是,此时 Fragment的状态依然由 FragmentManager 维护 -
attach()
重建 view 视图,附加到UI上显示 -
.addToBackStack()
是将事务放入回退栈,可以认为一个事务中可以有多个 Fragment
.addToBackStack()
做的是将这个事务放入回退栈。 类似于我们每添加一个 Activity 都会默认放入回退栈中,如果想要 Fragment 放入回退栈中,那么就需要借助启动 Fragment 的事务了,调用事务的.addToBackStack()
方法。 默认情况下,按下返回键的时候的操作是从回退栈中弹出一个事务。 按下返回键的时候,是从返回栈中弹出一个事务(Fragment)。显示栈顶的事务(对应的 Fragment) 如果当前栈顶的Fragment 已经执行过destroyView
了,则会重新执行生命周期(从createView
开始) 如果任务栈中只有一个Fragment,则此时按下返回键,进行的操作是把这个 Fragment 销毁。 在 Fragment A 中启动另一个 Fragment B 这个时候.addToBackStack()
是将 A 加入栈中。
基本使用
静态添加
// 直接在 xml 中添加
<fragment
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="200dp"
android:name="com.syd.good.feature.fragment.fragment.FragmentMy" />
在代码中获取的这个 Fragment
通过
Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.fragment);
注意静态添加 Fragment
的生命周期
E/FragmentMy: onAttach
E/FragmentMy: onCreate
E/FragmentMy: onCreateView
E/FragmentMy: onViewCreated
E/FragmentStaticActivity: onCreate()
E/FragmentMy: onActivityCreated
E/FragmentMy: onStart
E/FragmentStaticActivity: onStart()
E/FragmentStaticActivity: onResume()
E/FragmentMy: onResume
// 停止
E/FragmentMy: onPause
E/FragmentStaticActivity: onPause()
E/FragmentMy: onStop
E/FragmentStaticActivity: onStop()
E/FragmentMy: onDestroyView
E/FragmentMy: onDestroy
E/FragmentMy: onDetach()
E/FragmentStaticActivity: onDestroy()
动态添加
动态添加,一般都会选择加载到 FrameLayout
布局上,是因为 FrameLayout
布局足够简单,可以减少不必要的布局加载。
Fragment 的布局只是附着在 FrameLayout
上,作为 FrameLayout
的 子 View ,并没有取代 FrameLayout
<FrameLayout
android:id="@+id/fl"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
getSupportFragmentManager().beginTransaction()
.add(R.id.fl, new FragmentOne())
.add(R.id.fl, new FragmentTwo())
.commit();
Fragment 无论是否是全屏还是一半的,执行生命周期都一样
执行结果:
E/FragmentOne: onAttach
E/FragmentOne: onCreate
E/FragmentTwo: onAttach()
E/FragmentTwo: onCreate()
E/FragmentOne: onCreateView
E/FragmentOne: onViewCreated
E/FragmentOne: onActivityCreated
E/FragmentOne: onStart
E/FragmentOne: onResume
E/FragmentTwo: onCreateView
E/FragmentTwo: onActivityCreated
E/FragmentTwo: onStart
E/FragmentTwo: onResume
返回
E/FragmentOne: onPause
E/FragmentTwo: onPause
E/FragmentOne: onStop
E/FragmentTwo: onStop
E/FragmentOne: onDestroyView
E/FragmentOne: onDestroy
E/FragmentOne: onDetach()
E/FragmentTwo: onDestroyView
E/FragmentTwo: onDestroy
E/FragmentTwo: onDetach()
onHiddenChanged()
和普通的生命周期没有关系,只是调用 hidden
的时候会调用而已。
通过上面 .add
的方式添加 Fragment
只是将 Fragment 添加到指定的布局中,之后再添加 Fragment 是互相不影响的,在布局上看 FrameLayout
有两个子View(FragmentOne、FragmentTwo) 相互重叠。
关于.addToBackStack() 例子
例子1
主 Activity 中一次启动两个 Fragment ,并将这个事务加入到回退栈中。
getSupportFragmentManager().beginTransaction()
.add(R.id.fl, new FragmentOne())
.add(R.id.fl, new FragmentTwo())
.addToBackStack(null)
.commit();
这个时候按下返回键的结果是,从回退栈中弹出这个事务,意味这销毁这两个 Fragment
执行结果
Fragment 从 Activity 中脱离,留下 Activity
E/FragmentOne: onAttach
E/FragmentOne: onCreate
E/FragmentTwo: onAttach()
E/FragmentTwo: onCreate()
E/FragmentOne: onCreateView
E/FragmentOne: onViewCreated
E/FragmentOne: onActivityCreated
E/FragmentOne: onStart
E/FragmentOne: onResume
E/FragmentTwo: onCreateView
E/FragmentTwo: onActivityCreated
E/FragmentTwo: onStart
E/FragmentTwo: onResume
// 按下返回键
E/FragmentTwo: onPause
E/FragmentTwo: onStop
E/FragmentTwo: onDestroyView
E/FragmentTwo: onDestroy
E/FragmentTwo: onDetach()
E/FragmentOne: onPause
E/FragmentOne: onStop
E/FragmentOne: onDestroyView
E/FragmentOne: onDestroy
E/FragmentOne: onDetach()
假如没有 addToBackStack()
的时候,按下返回键,则直接退出 Activity 了。
例子2
在上面的基础上,在启动的 FragmentTwo 中添加方法
tvAdd.setOnClickListener((view1) -> {
Log.e("add", "add");
getFragmentManager().beginTransaction()
.add(R.id.fl, new FragmentThree())
.commit();
});
这个时候只是在 fl
中又添加了一个 FragmentThree
,并且这个事务没有加入到回退栈中,这个时候按下返回键,结果是:
页面没有发生任何改变,但是上一次保存在回退栈的事务被弹出了
E/FragmentTwo: onPause
E/FragmentTwo: onStop
E/FragmentTwo: onDestroyView
E/FragmentTwo: onDestroy
E/FragmentTwo: onDetach()
E/FragmentOne: onPause
E/FragmentOne: onStop
E/FragmentOne: onDestroyView
E/FragmentOne: onDestroy
E/FragmentOne: onDetach()
这个时候再按下返回键
Activity 就会被销毁。
例子3
在例子1的基础上,在添加 FragmentTwo 中添加方法
tvAdd.setOnClickListener((view1) -> {
Log.e("add", "add");
getFragmentManager().beginTransaction()
.add(R.id.fl, new FragmentThree())
// 添加了将此事务入栈
.addToBackStack(null)
.commit();
});
这个时候再按下返回键,是将这个栈顶的事务退出,这个时候例子1 中的事务就处于栈顶了。页面显示的就是例子1中的页面了。
再次按下返回键,是把例子1中的事务弹出,这个时候留下的就只有 Activity 了,再次按下返回键,弹出 Activity。
与 Activity 通信
- Activity 中有 Fragment 的引用,可以直接操作 Fragment 中的方法
- 每个 Fragment 都有一个唯一的 TAG或者 ID 可以通过
getFragmentManager.findFragmentByTag() 或者 findFragmentById()
获取 - Fragment 中通过
getActivity
获取当前绑定的 Activity 实例
Fragment 不应该直接操作其他的 Fragment。应该由管理者 Activity 来进行。
采用接口回调的形式
总结
凡是加入了回退栈 .addToBackStack(null)
之前加入的 Fragment 都不会被销毁。
比如:
Activity A 中
getSupportFragmentManager().beginTransaction()
.add(R.id.fl, new FragmentOne())
.add(R.id.fl, new FragmentTwo())
.commit();
FragmentTwo 中
getSupportFragmentManager().beginTransaction()
.replace(R.id.fl, new FragmentThree())
.addToBackStack(null)
.commit();
请注意 Activity A 中是没有 addToBackStack 的 ,即使这样在 FragmentTweo
中 replace
后 FragmentOne
和 FragmentTwo
也不会被销毁,而是走 onDestroyView()
方法。按下返回键后,恢复 FragmentOne
和 FragmentTwo
从 onCreateView
开始
如果没有 .addToBackStack(null)
的话,那么 FragmentOne
和 FragmentTwo
就会被销毁了。并且按下返回键的时候,就销毁掉 Activity 了。
页面1 addTo
页面2 没有
E/FragmentThree: onAttach() E/FragmentThree: onCreate() E/FragmentTwo: onPause E/FragmentTwo: onStop E/FragmentTwo: onDestroyView E/FragmentOne: onPause E/FragmentOne: onStop E/FragmentOne: onDestroyView E/FragmentThree: onCreateView E/FragmentThree: onActivityCreated E/FragmentThree: onStart E/FragmentThree: onResume E/FragmentTwo: onDestroy E/FragmentTwo: onDetach() E/FragmentOne: onDestroy E/FragmentOne: onDetach()
- 那些年,我们一起碰到过的骗局
- Spring Security (五) 动手实现一个IP_Login
- 史上最全Linux提权后获取敏感信息方法
- Spring Security (四) 核心过滤器源码分析
- Spring Security (三) 核心配置解读
- Spring Cloud配置中心获取不到最新配置信息的问题
- 总是听别人说响应式布局,原来这么简单
- Spring Cloud Zuul重试机制探秘
- Eureka中RetryableClientQuarantineRefreshPercentage参数探秘
- Edgware.RC1中ZuulFallbackProvider的改进
- JPA的多表复杂查询:详细篇
- 尝试使用Memcached遇到的狗血问题
- Enumerable#Zip 实现一下
- 更新自己,不要影响其他人
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- 详解 Numpy 中的视图和副本
- 详解 matplotlib 中的两种标注方法
- 混搭 TypeScript + GraphQL + DI + Decorator 风格写 Node.js 应用
- 彻底搞懂闭包,柯里化,手写代码,金九银十不再丢分!
- Kubernetes控制器--副本集ReplicaSet
- Awesome Kubernetes 系列:第一期
- Mongodb多键索引之数组文档
- 在 Cocos Creator 里画个炫酷的雷达图
- 用shader做一个柿子颜色的过场动画
- mysql 找出最新时间的一条数据
- 【NPM库】- 0x05 - 文件、路径操作
- MySQL中insert阻塞问题的分析
- Fedora32下编译安装Qemu5.1并创建ARM版本Linux虚拟机
- 面试官想问的HashMap,都在这一篇里面了!
- CentOS7下使用Mondo Rescue实现系统全备份