Fragment 事务

在运行时,FragmentManager 可以响应用户交互来添加、移除、替换 Fragment 并对其执行其他操作。您提交的每组 Fragment 变更都称为事务,您可以使用 FragmentTransaction 类提供的 API 指定事务内部的操作。您可以将多个操作分组到一个事务中,例如,一个事务可以添加或替换多个 Fragment。当您在同一屏幕上显示多个同级 Fragment 时(例如分屏视图),此分组非常有用。

您可以将每个事务保存到由 FragmentManager 管理的返回栈中,允许用户向后导航 Fragment 变更(类似于向后导航 activity)。

您可以从 FragmentManager 调用 beginTransaction() 来获取 FragmentTransaction 的实例,如以下示例所示:

Kotlin

val fragmentManager = ...
val fragmentTransaction = fragmentManager.beginTransaction()

Java

FragmentManager fragmentManager = ...
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

每个 FragmentTransaction 的最后一次调用必须提交该事务。commit() 调用会向 FragmentManager 发出信号,表示所有操作都已添加到事务中。

Kotlin

val fragmentManager = ...
// The fragment-ktx module provides a commit block that automatically
// calls beginTransaction and commit for you.
fragmentManager.commit {
    // Add operations here
}

Java

FragmentManager fragmentManager = ...
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();

// Add operations here

fragmentTransaction.commit();

允许重新排序 Fragment 状态变更

每个 FragmentTransaction 都应使用 setReorderingAllowed(true)

Kotlin

supportFragmentManager.commit {
    ...
    setReorderingAllowed(true)
}

Java

FragmentManager fragmentManager = ...
fragmentManager.beginTransaction()
    ...
    .setReorderingAllowed(true)
    .commit();

为了行为兼容性,重新排序标志默认不启用。但是,必须启用它才能让 FragmentManager 正确执行您的 FragmentTransaction,尤其是在它操作返回栈并运行动画和过渡时。启用该标志可确保如果多个事务同时执行,任何中间 Fragment(即已添加但立即被替换的 Fragment)不会经历生命周期变更,也不会执行其动画或过渡。请注意,此标志会影响事务的初始执行以及使用 popBackStack() 撤消事务。

添加和移除 Fragment

要将 Fragment 添加到 FragmentManager,请在事务上调用 add()。此方法接收 Fragment 的容器 ID 以及要添加的 Fragment 的类名。添加的 Fragment 会移动到 RESUMED 状态。强烈建议将容器设置为视图层次结构中的 FragmentContainerView

要从宿主中移除 Fragment,请调用 remove(),并传入通过 findFragmentById()findFragmentByTag() 从 Fragment 管理器检索到的 Fragment 实例。如果 Fragment 的视图之前已添加到容器中,则此时会将其从容器中移除。移除的 Fragment 会移动到 DESTROYED 状态。

使用 replace() 将容器中现有的 Fragment 替换为您提供的新 Fragment 类的实例。调用 replace() 等同于调用 remove() 并替换容器中的 Fragment,然后将新 Fragment 添加到同一容器中。

以下代码段显示了如何用一个 Fragment 替换另一个 Fragment:

Kotlin

// Create new fragment
val fragmentManager = // ...

// Create and commit a new transaction
fragmentManager.commit {
    setReorderingAllowed(true)
    // Replace whatever is in the fragment_container view with this fragment
    replace<ExampleFragment>(R.id.fragment_container)
}

Java

// Create new fragment and transaction
FragmentManager fragmentManager = ...
FragmentTransaction transaction = fragmentManager.beginTransaction();
transaction.setReorderingAllowed(true);

// Replace whatever is in the fragment_container view with this fragment
transaction.replace(R.id.fragment_container, ExampleFragment.class, null);

// Commit the transaction
transaction.commit();

在此示例中,ExampleFragment 的新实例将替换当前位于由 R.id.fragment_container 标识的布局容器中的 Fragment(如果有)。

默认情况下,FragmentTransaction 中所做的更改不会添加到返回栈中。要保存这些更改,您可以在 FragmentTransaction 上调用 addToBackStack()。有关详细信息,请参阅Fragment 管理器

提交是异步的

调用 commit() 不会立即执行事务。相反,事务被安排在主 UI 线程能够执行时尽快运行。但是,如有必要,您可以调用 commitNow() 以立即在 UI 线程上运行 Fragment 事务。

请注意,commitNowaddToBackStack 不兼容。另外,您可以调用 executePendingTransactions() 来执行所有尚未运行的、通过 commit() 调用提交的待处理 FragmentTransactions。此方法与 addToBackStack 兼容。

对于绝大多数用例,commit() 足以满足您的需求。

操作顺序很重要

FragmentTransaction 中执行操作的顺序很重要,尤其是在使用 setCustomAnimations() 时。此方法将给定的动画应用于其后所有 Fragment 操作。

Kotlin

supportFragmentManager.commit {
    setCustomAnimations(enter1, exit1, popEnter1, popExit1)
    add<ExampleFragment>(R.id.container) // gets the first animations
    setCustomAnimations(enter2, exit2, popEnter2, popExit2)
    add<ExampleFragment>(R.id.container) // gets the second animations
}

Java

getSupportFragmentManager().beginTransaction()
        .setCustomAnimations(enter1, exit1, popEnter1, popExit1)
        .add(R.id.container, ExampleFragment.class, null) // gets the first animations
        .setCustomAnimations(enter2, exit2, popEnter2, popExit2)
        .add(R.id.container, ExampleFragment.class, null) // gets the second animations
        .commit()

限制 Fragment 的生命周期

FragmentTransactions 可以影响事务范围内添加的单个 Fragment 的生命周期状态。创建 FragmentTransaction 时,setMaxLifecycle() 为给定 Fragment 设置最大状态。例如,ViewPager2 使用 setMaxLifecycle() 将屏幕外 Fragment 限制为 STARTED 状态。

显示和隐藏 Fragment 的视图

使用 FragmentTransaction 方法 show()hide() 显示和隐藏已添加到容器中的 Fragment 的视图。这些方法设置 Fragment 视图的可见性,而影响 Fragment 的生命周期。

虽然您不需要使用 Fragment 事务来切换 Fragment 内视图的可见性,但这些方法对于您希望可见性状态的更改与返回栈上的事务相关联的情况非常有用。

附加和分离 Fragment

FragmentTransaction 方法 detach() 将 Fragment 从界面分离,销毁其视图层次结构。Fragment 保持与放入返回栈时相同的状态 (STOPPED)。这意味着 Fragment 已从界面中移除,但仍由 Fragment 管理器管理。

attach() 方法重新附加之前已分离的 Fragment。这会导致其视图层次结构重新创建、附加到界面并显示。

由于 FragmentTransaction 被视为一组原子操作,因此在同一事务中对同一 Fragment 实例进行 detachattach 调用会相互抵消,从而避免 Fragment 界面被销毁并立即重新创建。如果您想分离然后立即重新附加 Fragment,请使用单独的事务,如果使用 commit(),则用 executePendingOperations() 分隔。