片段事务

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

您可以将每个事务保存到由 FragmentManager 管理的后退栈中,允许用户向后浏览片段更改,这类似于在活动之间向后导航。

您可以通过调用 beginTransaction()FragmentManager 获取 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();

允许重新排序片段状态更改

每个 FragmentTransaction 应使用 setReorderingAllowed(true)

Kotlin

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

Java

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

为了行为兼容性,默认情况下不会启用重新排序标志。但是,它需要允许 FragmentManager 正确执行您的 FragmentTransaction,尤其是在它操作后退栈并运行动画和过渡时。启用该标志可确保如果一起执行多个事务,任何中间片段(即添加然后立即替换的片段)都不会经历生命周期更改或执行其动画或过渡。请注意,此标志会影响事务的初始执行以及使用 popBackStack() 反转事务。

添加和删除片段

要将片段添加到 FragmentManager,请在事务上调用 add()。此方法接收片段的容器的 ID,以及您要添加的片段的类名。添加的片段被移动到 RESUMED 状态。强烈建议容器是视图层次结构的一部分的 FragmentContainerView

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

使用 replace() 用您提供的新的片段类的实例替换容器中现有的片段。调用 replace() 等效于对容器中的片段调用 remove() 并将新片段添加到同一容器中。

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

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 标识的布局容器中的片段(如果有)。

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

提交是异步的

调用 commit() 不会立即执行事务。相反,事务被安排在主 UI 线程上尽快运行。但是,如果需要,您可以调用 commitNow() 以立即在您的 UI 线程上运行片段事务。

请注意,commitNowaddToBackStack 不兼容。或者,您可以通过调用 executePendingTransactions() 来执行所有由 commit() 调用提交但尚未运行的挂起 FragmentTransactions。这种方法与 addToBackStack 兼容。

对于绝大多数用例,commit() 就足够了。

操作顺序很重要

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

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()

限制片段的生命周期

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

显示和隐藏片段的视图

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

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

附加和分离片段

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

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

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