FragmentManager
是负责对您应用的 Fragment 执行操作的类,例如添加、移除或替换 Fragment 以及将其添加到返回堆栈。
如果您使用的是 Jetpack Navigation 库,您可能永远不会直接与 FragmentManager
交互,因为它会代表您使用 FragmentManager
。但是,任何使用 Fragment 的应用都会在某种程度上使用 FragmentManager
,因此了解它是什么以及它是如何工作的非常重要。
本页内容包括:
- 如何访问
FragmentManager
。 FragmentManager
在您的 Activity 和 Fragment 中的作用。- 如何使用
FragmentManager
管理返回堆栈。 - 如何为 Fragment 提供数据和依赖项。
访问 FragmentManager
您可以从 Activity 或 Fragment 访问 FragmentManager
。
FragmentActivity
及其子类(如 AppCompatActivity
)可以通过 getSupportFragmentManager()
方法访问 FragmentManager
。
Fragment 可以托管一个或多个子 Fragment。在 Fragment 中,您可以通过 getChildFragmentManager()
获取管理 Fragment 子级的 FragmentManager
引用。如果您需要访问其宿主 FragmentManager
,可以使用 getParentFragmentManager()
。
下面是几个示例,展示了 Fragment、其宿主以及与它们关联的 FragmentManager
实例之间的关系。

图 1 展示了两个示例,每个示例都有一个单一的 Activity 宿主。这两个示例中的宿主 Activity 都将顶级导航作为 BottomNavigationView
显示给用户,该导航负责在应用中用不同的屏幕替换宿主 Fragment。每个屏幕都实现为单独的 Fragment。
示例 1 中的宿主 Fragment 托管两个子 Fragment,它们构成一个分屏视图。示例 2 中的宿主 Fragment 托管一个子 Fragment,它构成滑动视图的显示 Fragment。
有了这个设置,您可以将每个宿主视为拥有一个与其关联的 FragmentManager
,用于管理其子 Fragment。图 2 对此进行了说明,其中还包括 supportFragmentManager
、parentFragmentManager
和 childFragmentManager
之间的属性映射。

FragmentManager
,用于管理其子 Fragment。要引用的相应 FragmentManager
属性取决于调用站点在 Fragment 层次结构中的位置以及您要访问的 Fragment 管理器。
一旦您获得了 FragmentManager
的引用,就可以使用它来操作向用户显示的 Fragment。
子 Fragment
一般来说,您的应用项目由一个或少量 Activity 组成,每个 Activity 代表一组相关的屏幕。Activity 可以提供一个放置顶级导航的位置,以及一个在 Fragment 之间限定 ViewModel
对象和其他视图状态的作用域。Fragment 代表应用中的一个独立目标。
如果您想一次显示多个 Fragment,例如在分屏视图或信息中心中,您可以使用由您的目标 Fragment 及其子 Fragment 管理器管理的子 Fragment。
子 Fragment 的其他用例如下:
- 屏幕滑动,在父 Fragment 中使用
ViewPager2
管理一系列子 Fragment 视图。 - 一组相关屏幕内的子导航。
- Jetpack Navigation 使用子 Fragment 作为独立目标。一个 Activity 托管一个父
NavHostFragment
,并在用户浏览您的应用时,用不同的子目标 Fragment 填充其空间。
使用 FragmentManager
A FragmentManager
管理 Fragment 返回堆栈。在运行时,FragmentManager
可以响应用户交互执行返回堆栈操作,例如添加或移除 Fragment。每组更改作为一个称为 FragmentTransaction
的单元一起提交。有关 Fragment 事务的更深入讨论,请参阅Fragment 事务指南。
当用户点按设备上的“返回”按钮,或者当您调用 FragmentManager.popBackStack()
时,最顶层的 Fragment 事务会从堆栈中弹出。如果堆栈中没有更多的 Fragment 事务,并且您没有使用子 Fragment,则返回事件会冒泡到 Activity。如果您正在使用子 Fragment,请参阅子 Fragment 和同级 Fragment 的特殊注意事项。
当您在事务上调用 addToBackStack()
时,该事务可以包含任意数量的操作,例如添加多个 Fragment 或替换多个容器中的 Fragment。
当返回堆栈弹出时,所有这些操作都会作为一个原子操作逆转。但是,如果您在调用 popBackStack()
之前提交了其他事务,并且您没有为该事务使用 addToBackStack()
,则这些操作不会逆转。因此,在单个 FragmentTransaction
中,请避免将影响返回堆栈的事务与不影响返回堆栈的事务交错。
执行事务
要在布局容器中显示 Fragment,请使用 FragmentManager
创建一个 FragmentTransaction
。在事务中,您可以对容器执行 add()
或 replace()
操作。
例如,一个简单的 FragmentTransaction
可能如下所示:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("name") // Name can be null }
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack("name") // Name can be null .commit();
在此示例中,ExampleFragment
替换了当前位于由 R.id.fragment_container
ID 标识的布局容器中的 Fragment(如果有)。将 Fragment 的类提供给 replace()
方法可让 FragmentManager
使用其 FragmentFactory
处理实例化。有关详细信息,请参阅为 Fragment 提供依赖项部分。
setReorderingAllowed(true)
可优化事务中涉及的 Fragment 的状态更改,以便动画和过渡效果正常工作。有关使用动画和过渡进行导航的更多信息,请参阅Fragment 事务和使用动画在 Fragment 之间导航。
调用 addToBackStack()
会将事务提交到返回堆栈。用户稍后可以通过点按“返回”按钮来撤销事务并恢复之前的 Fragment。如果您在单个事务中添加或移除了多个 Fragment,则当返回堆栈弹出时,所有这些操作都会被撤销。在 addToBackStack()
调用中提供的可选名称使您能够使用 popBackStack()
返回到特定事务。
如果您在执行移除 Fragment 的事务时未调用 addToBackStack()
,则被移除的 Fragment 在事务提交时会被销毁,用户无法导航回它。如果您在移除 Fragment 时调用了 addToBackStack()
,则该 Fragment 仅处于 STOPPED
状态,并在用户导航回来时稍后 RESUMED
。在这种情况下,其视图会被销毁。有关详细信息,请参阅Fragment 生命周期。
查找现有 Fragment
您可以使用 findFragmentById()
获取布局容器中当前 Fragment 的引用。使用 findFragmentById()
可以通过从 XML 膨胀时给定的 ID 或在 FragmentTransaction
中添加时容器的 ID 来查找 Fragment。示例如下:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentById(R.id.fragment_container);
或者,您可以为 Fragment 分配一个唯一标签,并使用 findFragmentByTag()
获取引用。您可以使用 android:tag
XML 属性在布局中定义的 Fragment 上,或在 FragmentTransaction
内的 add()
或 replace()
操作期间分配标签。
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container, "tag") setReorderingAllowed(true) addToBackStack(null) } ... val fragment: ExampleFragment = supportFragmentManager.findFragmentByTag("tag") as ExampleFragment
Java
FragmentManager fragmentManager = getSupportFragmentManager(); fragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null, "tag") .setReorderingAllowed(true) .addToBackStack(null) .commit(); ... ExampleFragment fragment = (ExampleFragment) fragmentManager.findFragmentByTag("tag");
子 Fragment 和同级 Fragment 的特殊注意事项
在任何给定时间,只有一个 FragmentManager
可以控制 Fragment 返回堆栈。如果您的应用同时在屏幕上显示多个同级 Fragment,或者如果您的应用使用子 Fragment,则会指定一个 FragmentManager
来处理您应用的主要导航。
要在 Fragment 事务中定义主导航 Fragment,请在该事务上调用 setPrimaryNavigationFragment()
方法,传入其 childFragmentManager
拥有主控制权的 Fragment 实例。
将导航结构视为一系列层,其中 Activity 是最外层,包裹着下面的每个子 Fragment 层。每个层都有一个主导航 Fragment。
当“返回”事件发生时,最内层控制导航行为。一旦最内层没有更多的 Fragment 事务可以弹出,控制权就会返回到下一层,这个过程会重复进行,直到您到达 Activity。
当同时显示两个或更多 Fragment 时,只有一个是主导航 Fragment。将 Fragment 设置为主导航 Fragment 会移除之前 Fragment 的指定。以上述示例为例,如果您将详细信息 Fragment 设置为主导航 Fragment,则主 Fragment 的指定将被移除。
支持多个返回堆栈
在某些情况下,您的应用可能需要支持多个返回堆栈。一个常见的示例是您的应用使用底部导航栏。FragmentManager
允许您使用 saveBackStack()
和 restoreBackStack()
方法支持多个返回堆栈。这些方法允许您通过保存一个返回堆栈并恢复另一个返回堆栈来在返回堆栈之间切换。
saveBackStack()
的工作方式类似于调用带可选 name
参数的 popBackStack()
:指定的事务以及堆栈中其之后的所有事务都会弹出。不同之处在于 saveBackStack()
会保存已弹出事务中所有 Fragment 的状态。
例如,假设您之前通过使用 addToBackStack()
提交 FragmentTransaction
将 Fragment 添加到返回堆栈,如以下示例所示:
Kotlin
supportFragmentManager.commit { replace<ExampleFragment>(R.id.fragment_container) setReorderingAllowed(true) addToBackStack("replacement") }
Java
supportFragmentManager.beginTransaction() .replace(R.id.fragment_container, ExampleFragment.class, null) // setReorderingAllowed(true) and the optional string argument for // addToBackStack() are both required if you want to use saveBackStack() .setReorderingAllowed(true) .addToBackStack("replacement") .commit();
在这种情况下,您可以通过调用 saveBackStack()
来保存此 Fragment 事务和 ExampleFragment
的状态。
Kotlin
supportFragmentManager.saveBackStack("replacement")
Java
supportFragmentManager.saveBackStack("replacement");
您可以使用相同的名称参数调用 restoreBackStack()
来恢复所有已弹出的事务和所有已保存的 Fragment 状态。
Kotlin
supportFragmentManager.restoreBackStack("replacement")
Java
supportFragmentManager.restoreBackStack("replacement");
为 Fragment 提供依赖项
添加 Fragment 时,您可以手动实例化 Fragment 并将其添加到 FragmentTransaction
。
Kotlin
fragmentManager.commit { // Instantiate a new instance before adding val myFragment = ExampleFragment() add(R.id.fragment_view_container, myFragment) setReorderingAllowed(true) }
Java
// Instantiate a new instance before adding ExampleFragment myFragment = new ExampleFragment(); fragmentManager.beginTransaction() .add(R.id.fragment_view_container, myFragment) .setReorderingAllowed(true) .commit();
当您提交 Fragment 事务时,您创建的 Fragment 实例就是使用的实例。但是,在配置更改期间,您的 Activity 及其所有 Fragment 都会被销毁,然后使用最适用的Android 资源重新创建。FragmentManager
会为您处理所有这些:它会重新创建您的 Fragment 实例,将它们附加到宿主,并重新创建返回堆栈状态。
默认情况下,FragmentManager
使用框架提供的 FragmentFactory
来实例化您的 Fragment 的新实例。此默认工厂使用反射来查找和调用您的 Fragment 的无参数构造函数。这意味着您不能使用此默认工厂为您的 Fragment 提供依赖项。它还意味着您第一次用于创建 Fragment 的任何自定义构造函数,默认情况下在重新创建时都不会使用。
要为 Fragment 提供依赖项,或使用任何自定义构造函数,请改为创建自定义 FragmentFactory
子类,然后重写 FragmentFactory.instantiate
。然后,您可以使用您的自定义工厂覆盖 FragmentManager
的默认工厂,该工厂随后用于实例化您的 Fragment。
假设您有一个 DessertsFragment
,它负责显示您家乡的流行甜点,并且 DessertsFragment
依赖于一个 DessertsRepository
类,该类为它提供向用户显示正确界面所需的信息。
您可以将 DessertsFragment
定义为在其构造函数中需要一个 DessertsRepository
实例。
Kotlin
class DessertsFragment(val dessertsRepository: DessertsRepository) : Fragment() { ... }
Java
public class DessertsFragment extends Fragment { private DessertsRepository dessertsRepository; public DessertsFragment(DessertsRepository dessertsRepository) { super(); this.dessertsRepository = dessertsRepository; } // Getter omitted. ... }
您的 FragmentFactory
的简单实现可能如下所示。
Kotlin
class MyFragmentFactory(val repository: DessertsRepository) : FragmentFactory() { override fun instantiate(classLoader: ClassLoader, className: String): Fragment = when (loadFragmentClass(classLoader, className)) { DessertsFragment::class.java -> DessertsFragment(repository) else -> super.instantiate(classLoader, className) } }
Java
public class MyFragmentFactory extends FragmentFactory { private DessertsRepository repository; public MyFragmentFactory(DessertsRepository repository) { super(); this.repository = repository; } @NonNull @Override public Fragment instantiate(@NonNull ClassLoader classLoader, @NonNull String className) { Class<? extends Fragment> fragmentClass = loadFragmentClass(classLoader, className); if (fragmentClass == DessertsFragment.class) { return new DessertsFragment(repository); } else { return super.instantiate(classLoader, className); } } }
此示例对 FragmentFactory
进行了子类化,重写了 instantiate()
方法,为 DessertsFragment
提供了自定义的 Fragment 创建逻辑。其他 Fragment 类通过 FragmentFactory
的默认行为(通过 super.instantiate()
)进行处理。
然后,您可以通过在 FragmentManager
上设置属性,将 MyFragmentFactory
指定为构建应用 Fragment 时要使用的工厂。您必须在 Activity 的 super.onCreate()
之前设置此属性,以确保在重新创建 Fragment 时使用 MyFragmentFactory
。
Kotlin
class MealActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { supportFragmentManager.fragmentFactory = MyFragmentFactory(DessertsRepository.getInstance()) super.onCreate(savedInstanceState) } }
Java
public class MealActivity extends AppCompatActivity { @Override protected void onCreate(@Nullable Bundle savedInstanceState) { DessertsRepository repository = DessertsRepository.getInstance(); getSupportFragmentManager().setFragmentFactory(new MyFragmentFactory(repository)); super.onCreate(savedInstanceState); } }
在 Activity 中设置 FragmentFactory
会覆盖 Activity 整个 Fragment 层次结构中的 Fragment 创建。换句话说,您添加的任何子 Fragment 的 childFragmentManager
都会使用此处设置的自定义 Fragment 工厂,除非在较低级别被覆盖。
使用 FragmentFactory 进行测试
在单个 Activity 架构中,使用 FragmentScenario
类隔离测试您的 Fragment。由于您不能依赖 Activity 的自定义 onCreate
逻辑,因此您可以将 FragmentFactory
作为参数传递给 Fragment 测试,如以下示例所示:
// Inside your test val dessertRepository = mock(DessertsRepository::class.java) launchFragment<DessertsFragment>(factory = MyFragmentFactory(dessertRepository)).onFragment { // Test Fragment logic }
有关此测试过程的详细信息和完整示例,请参阅测试您的 Fragment。