文章目录
-
- [一、什么是 Fragment](#一、什么是 Fragment)
-
- [1.1 Fragment 的概念](#1.1 Fragment 的概念)
- [1.2 Fragment 的优势](#1.2 Fragment 的优势)
- [二、第一个 Fragment](#二、第一个 Fragment)
-
- [2.1 创建 Fragment 类](#2.1 创建 Fragment 类)
- [2.2 编写 Fragment 布局](#2.2 编写 Fragment 布局)
- [2.3 在 Activity 中加载 Fragment ------ 静态方式](#2.3 在 Activity 中加载 Fragment —— 静态方式)
- [三、动态添加 Fragment](#三、动态添加 Fragment)
-
- [3.1 为什么需要动态加载](#3.1 为什么需要动态加载)
- [3.2 FragmentManager 介绍](#3.2 FragmentManager 介绍)
- [3.3 动态添加 Fragment](#3.3 动态添加 Fragment)
- [四、Fragment 事务与返回栈](#四、Fragment 事务与返回栈)
-
- [4.1 FragmentTransaction 常用方法](#4.1 FragmentTransaction 常用方法)
- [4.2 返回栈机制](#4.2 返回栈机制)
- [4.3 返回栈执行流程分析](#4.3 返回栈执行流程分析)
- [五、Fragment 与 Activity 通信](#五、Fragment 与 Activity 通信)
-
- [5.1 Fragment 中获取 Activity](#5.1 Fragment 中获取 Activity)
- [5.2 Activity 中获取 Fragment](#5.2 Activity 中获取 Fragment)
- [5.3 Fragment 之间通信](#5.3 Fragment 之间通信)
- [六、Fragment 的生命周期](#六、Fragment 的生命周期)
-
- [6.1 生命周期概览](#6.1 生命周期概览)
- [6.2 Fragment 生命周期与 Activity 对比](#6.2 Fragment 生命周期与 Activity 对比)
一、什么是 Fragment
1.1 Fragment 的概念
Fragment 是什么?
Fragment 从字面上翻译是"碎片",它其实就是一个可以嵌入在 Activity 中的 UI 片段。一个 Activity 可以由多个 Fragment 拼凑而成,Fragment 有自己的布局、自己的生命周期,但它必须依附于 Activity 才能存在。
Fragment 和 Activity 的关系很像是"零件"和"整体":Activity 像一个屏幕窗口,Fragment 就像是窗口里的一块一块模块。平板上的一个应用,左边是列表,右边是内容,这两个区域就可以分别用两个 Fragment 来实现,最后放到同一个 Activity 里。
为什么 Google 要推出 Fragment?
早期的 Android 开发,界面全靠 Activity,可不同屏幕尺寸的适配非常头疼。手机上一个列表页 + 详情页要分两个 Activity,到了平板上完全可以把列表和详情放在同一屏,这就需要有更灵活的 UI 模块。于是 Fragment 应运而生,它天生就适合用来解决多屏幕适配和界面模块化的问题。
1.2 Fragment 的优势
-
提高界面复用性
同一个 Fragment 可以放到不同的 Activity 中,不用重复写布局和逻辑。比如一个"用户信息"Fragment,可以在多个页面引用。
-
适配平板和手机等不同尺寸设备
平板可以同时显示多个 Fragment,而手机可以一次只显示一个,但核心代码不用变,只需调整布局。
-
降低 Activity 的复杂度
把复杂的界面拆分成一个个小 Fragment,每个 Fragment 只负责自己那一小块的逻辑,Activity 更多是作为一个容器,代码清晰得多。
二、第一个 Fragment
下面我们从一个最简单的例子开始,创建一个 Fragment 并显示出来。
2.1 创建 Fragment 类
新建一个类 LeftFragment,继承自 Fragment(注意要用 androidx.fragment.app.Fragment),并重写 onCreateView() 方法。
kotlin
class LeftFragment : Fragment() {
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.left_fragment, container, false)
}
}
onCreateView() 里我们通过 LayoutInflater 把定义好的布局文件加载进来,最后一个参数 false 表示不立即把该 View 添加到 container 中,而是让系统去处理。
2.2 编写 Fragment 布局
创建对应的布局文件 left_fragment.xml:
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="This is left fragment"
android:textSize="18sp" />
</LinearLayout>
2.3 在 Activity 中加载 Fragment ------ 静态方式
在 Activity 的布局中直接使用 <fragment> 标签即可静态添加 Fragment:
xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<fragment
android:id="@+id/left_fragment"
android:name="com.example.fragmenttest.LeftFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
android:name 指定了我们要加载的 Fragment 全类名,这样就完成了最简单的静态加载。
静态加载虽然简单,但不够灵活,实际开发中更多是用后面要讲的动态加载。
三、动态添加 Fragment
3.1 为什么需要动态加载
静态加载的 Fragment 是写死在布局中的,而实际的 App 经常需要根据用户点击、状态变化去替换或添加界面模块,比如点击某个按钮后切换内容区域,这时候就需要动态加载 Fragment。
3.2 FragmentManager 介绍
动态操作 Fragment 离不开两个核心类:
- FragmentManager :管理 Fragment 的添加、移除、替换等操作,可以通过
getSupportFragmentManager()获得(在 AppCompatActivity 中)。 - FragmentTransaction:代表一组 Fragment 操作的事务,通过它把添加、替换等操作组合在一起,然后提交执行。
3.3 动态添加 Fragment
典型步骤:创建 Fragment 实例 → 开启事务 → 执行操作 → 提交事务。
以下是把布局容器 R.id.right_layout 中的内容替换为 AnotherRightFragment 的例子:
kotlin
val fragmentManager = supportFragmentManager
val transaction = fragmentManager.beginTransaction()
transaction.replace(R.id.right_layout, AnotherRightFragment())
transaction.commit()
replace()会先移除容器中的旧 Fragment,再添加新的。- 也可以使用
add()往容器里追加一个新的 Fragment。 - 最后一定要调用
commit(),否则所有操作不会生效。
四、Fragment 事务与返回栈
4.1 FragmentTransaction 常用方法
在事务中我们经常会用到:
add(容器id, Fragment): 添加一个 Fragmentreplace(容器id, Fragment): 替换一个 Fragmentremove(Fragment): 移除一个 Fragmenthide(Fragment)/show(Fragment): 隐藏/显示 Fragment,适合做 Tab 切换addToBackStack(null): 将当前事务加入返回栈
4.2 返回栈机制
为什么需要返回栈?
想象一下,我们通过 replace() 切换到了新 Fragment,此时用户按返回键,默认行为是直接退出 Activity。但通常我们希望按返回键能回到上一个 Fragment,就像页面回退一样。
解决办法就是调用:
kotlin
transaction.addToBackStack(null)
这段代码的意思是:在执行这个事务前,先把当前 Fragment 的状态保存到返回栈里,当用户按返回键时,系统会从栈里弹出上一个状态并恢复。
4.3 返回栈执行流程分析
-
不加入返回栈 :
replace()后按返回键 → Activity finish,直接退出。 -
加入返回栈 :
replace()前调用了addToBackStack(null)→ 按返回键 → 回退到上一个 Fragment,再按才会退出 Activity。
这个机制对用户体验非常友好,尤其在多个 Fragment 切换的场景下几乎是必用的。
五、Fragment 与 Activity 通信
5.1 Fragment 中获取 Activity
Fragment 中可以通过 getActivity() 获取到宿主 Activity,然后调用 Activity 里的方法或访问数据:
kotlin
val activity = activity as? MainActivity
activity?.doSomething()
注意判空,因为当 Fragment 已经 detach 时,
getActivity()会返回 null。
5.2 Activity 中获取 Fragment
Activity 想要获取某个 Fragment 实例,可以通过 FragmentManager 的 findFragmentById():
kotlin
val fragment = supportFragmentManager
.findFragmentById(R.id.right_fragment) as? RightFragment
拿到 Fragment 实例后,就能调用它内部公开的方法了。
5.3 Fragment 之间通信
Fragment 之间不推荐直接通信 ,因为这样耦合性太强。正确的做法是通过 Activity 作为中介:
- Fragment A 将事件传递给 Activity;
- Activity 收到事件后再去调用 Fragment B 的对应方法。
这样两个 Fragment 都只和 Activity 打交道,保持了独立性。
六、Fragment 的生命周期
6.1 生命周期概览
Fragment 的生命周期方法比 Activity 多了一些和视图创建/销毁相关的:
onAttach()------ Fragment 和 Activity 关联时调用onCreate()------ Fragment 被创建onCreateView()------ 创建并返回 Fragment 的视图层次结构onViewCreated()------ 视图创建完毕onActivityCreated()------ Activity 的onCreate()已执行完onStart()------ Fragment 可见(但可能没在前台)onResume()------ Fragment 可见且可交互onPause()------ 失去焦点onStop()------ 不再可见onDestroyView()------ Fragment 视图被移除onDestroy()------ Fragment 自身被销毁onDetach()------ 与 Activity 解除关联
6.2 Fragment 生命周期与 Activity 对比
| Activity | Fragment |
|---|---|
| onCreate | onCreate |
| onCreateView | |
| onViewCreated | |
| onStart | onStart |
| onResume | onResume |
| onPause | onPause |
| onStop | onStop |
| onDestroy | onDestroyView |
| onDestroy | |
| onDetach |
可以看到,Fragment 的生命周期基本被 Activity 的生命周期所包裹,多出来的是 View 的创建与销毁阶段。特别注意,onCreateView~onDestroyView 这一对只在视图层面操作,不会销毁 Fragment 本身,因此可以单独重建视图。