片段事务

在运行时,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 的销毁和立即重新创建。如果您想分离然后立即重新附加片段,请使用分开的、由 executePendingOperations() 分隔的事务(如果使用 commit())。