Android Fragment(碎片)详解

文章目录

    • [一、什么是 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) : 添加一个 Fragment
  • replace(容器id, Fragment) : 替换一个 Fragment
  • remove(Fragment) : 移除一个 Fragment
  • hide(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 实例,可以通过 FragmentManagerfindFragmentById()

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 多了一些和视图创建/销毁相关的:

  1. onAttach() ------ Fragment 和 Activity 关联时调用
  2. onCreate() ------ Fragment 被创建
  3. onCreateView() ------ 创建并返回 Fragment 的视图层次结构
  4. onViewCreated() ------ 视图创建完毕
  5. onActivityCreated() ------ Activity 的 onCreate() 已执行完
  6. onStart() ------ Fragment 可见(但可能没在前台)
  7. onResume() ------ Fragment 可见且可交互
  8. onPause() ------ 失去焦点
  9. onStop() ------ 不再可见
  10. onDestroyView() ------ Fragment 视图被移除
  11. onDestroy() ------ Fragment 自身被销毁
  12. 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 的创建与销毁阶段。特别注意,onCreateViewonDestroyView 这一对只在视图层面操作,不会销毁 Fragment 本身,因此可以单独重建视图。