Android中Fragment
Fragment
Fragment表示应用界面中可重复使用的一部分。Fragment 定义和管理自己的布局,具有自己的生命周期,并且可以处理自己的输入事件。Fragment 不能独立存在,而是必须由 Activity 或另一个 Fragment 托管。Fragment 的视图层次结构会成为宿主的视图层次结构的一部分,或附加到宿主的视图层次结构。
模块化
Fragment 允许您将界面划分为离散的区块,从而将模块化和可重用性引入 Activity 的界面。Activity 是围绕应用的界面放置全局元素(如抽屉式导航栏)的理想位置。相反,Fragment 更适合定义和管理单个屏幕或部分屏幕的界面。
假设有一个响应各种屏幕尺寸的应用。在较大的屏幕上,该应用应显示一个静态抽屉式导航栏和一个采用网格布局的列表。在较小的屏幕上,该应用应显示一个底部导航栏和一个采用线性布局的列表。在 Activity 中管理所有这些变化因素可能会很麻烦。将导航元素与内容分离可使此过程更易于管理。然后,Activity 负责显示正确的导航界面,而 Fragment 采用适当的布局显示列表。
创建Fragment
Fragment表示活动中用户界面的模块化部分。片段有自己的生命周期,接收自己的输入事件,您可以在包含的活动运行时添加或删除片段。
创建一个Fragment类
class ExampleFragment : Fragment(R.layout.example_fragment)
Fragment 库还提供了更专业的片段基类:
DialogFragment
显示一个浮动对话框。使用此类创建对话框是在 Activity类中使用对话框帮助器方法的一个很好的替代方法,因为片段会自动处理Dialog. 有关更多详细信息,请参阅显示对话框DialogFragment 。
PerferenceFragmentCompat
将对象的层次结构显示 Preference为列表。您可以使用PreferenceFragmentCompat为 您的应用创建设置屏幕.
将Fragment添加到活动
通常,您的 Fragment 必须嵌入到 AndroidX FragmentActivity中,才能为该活动的布局贡献一部分 UI。FragmentActivity
是 的基类 AppCompatActivity
,因此如果您已经在进行子类化AppCompatActivity
以在应用程序中提供向后兼容性,则无需更改活动基类。
您可以通过在活动的布局文件中定义片段或在活动的布局文件中定义片段容器然后以编程方式从活动中添加片段来将片段添加到活动的视图层次结构中。无论哪种情况,您都需要添加一个 FragmentContainerView
定义Fragment
应放置在活动视图层次结构中的位置。强烈建议始终使用 a FragmentContainerView
作为Fragment
的容器,因为 FragmentContainerView
其中包括特定于其他视图组(例如FrameLayout
不提供)的Fragment
的修复。
通过XML添加Fragment
要以声明方式将Fragment
添加到活动布局的 XML,请使用 FragmentContainerView元素。
这是一个包含单个 Activity 布局的示例 FragmentContainerView:
<!-- res/layout/example_activity.xml -->
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:name="com.example.ExampleFragment" />
该属性指定要实例化android:name
的类名。Fragment
当活动的布局膨胀时,指定的Fragment
被实例化, onInflate()
在新实例化的片段上被调用,并FragmentTransaction
创建一个将片段添加到FragmentManager
。
以编程方式添加Fragment
要以编程方式将片段添加到 Activity 的布局中,布局应包含FragmentContainerView用作片段容器的 ,如以下示例所示:
<!-- res/layout/example_activity.xml -->
<androidx.fragment.app.FragmentContainerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
与 XML 方法不同,此处android:name
不使用属性 FragmentContainerView
,因此不会自动实例化特定的片段。相反, FragmentTransaction
用于实例化片段并将其添加到活动的布局中。
当您的活动正在运行时,您可以进行片段事务,例如添加、删除或替换片段。在您的FragmentActivity
中,您可以获得 的实例,该实例 FragmentManager
可用于创建FragmentTransaction
. 然后,您可以在 Activity 的onCreate()
方法中使用 实例化您的片段FragmentTransaction.add()
,传入ViewGroup
布局中容器的 ID 和要添加的片段类,然后提交事务,如下例所示:
class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
supportFragmentManager.commit {
setReorderingAllowed(true)
add<ExampleFragment>(R.id.fragment_container_view)
}
}
}
}
在前面的示例中,请注意片段事务仅在savedInstanceStateis时创建null。这是为了确保在首次创建活动时只添加一次片段。当发生配置更改并重新创建活动时, savedInstanceState不再是null,并且不需要第二次添加片段,因为片段会自动从savedInstanceState.
如果您的片段需要一些初始数据,则可以通过Bundle
在对的调用中提供 ,来将参数传递给您的片段FragmentTransaction.add()
,如下所示:
class ExampleActivity : AppCompatActivity(R.layout.example_activity) {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (savedInstanceState == null) {
val bundle = bundleOf("some_int" to 0)
supportFragmentManager.commit {
setReorderingAllowed(true)
add<ExampleFragment>(R.id.fragment_container_view, args = bundle)
}
}
}
}
Bundle
然后可以通过调用从片段中检索 参数requireArguments()
,并且可以使用适当的Bundle getter
方法来检索每个参数。
class ExampleFragment : Fragment(R.layout.example_fragment) {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
val someInt = requireArguments().getInt("some_int")
...
}
}
Fragment 管理器
FragmentManager
类负责对应用的 Fragment 执行一些操作,如添加、移除或替换它们,以及将它们添加到返回堆栈。
如果您使用的是 Jetpack Navigation 库,则可能永远不会直接与 FragmentManager
交互,因为该库会代表您使用 FragmentManager
。也就是说,任何使用 Fragment 的应用都在某种程度上使用 FragmentManager
,因此了解它是什么以及它如何工作非常重要。
如何访问 FragmentManager、FragmentManager 与 Activity 和 Fragment 相关的角色、如何使用 FragmentManager 管理返回堆栈,以及如何为 Fragment 提供数据和依赖项。
访问 FragmentManager
在Activity中访问
每个 FragmentActivity
及其子类(如 AppCompatActivity
)都可以通过 getSupportFragmentManager()
方法访问 FragmentManager
。
在 Fragment 中访问
Fragment 也能够托管一个或多个子 Fragment。在 Fragment 内,您可以通过 getChildFragmentManager()
获取对管理 Fragment 子级的 FragmentManager
的引用。如果您需要访问其宿主 FragmentManager
,可以使用 getParentFragmentManager()
。
子Fragment
一般来说,应用应由应用项目中的一个或少数几个 Activity 组成,其中每个 Activity 表示一组相关的屏幕。Activity 可能会提供一个点来放置顶级导航,并提供一个位置来限定 ViewModels
以及 Fragment 之间的其他视图状态的范围。应用中的每个目的地应由一个 Fragment 表示。
如果您想要一次显示多个 Fragment(如在拆分视图中或信息中心内),应使用子 Fragment,它们由目的地 Fragment 及其子 Fragment 管理器进行管理。
子 Fragment 的其他用例可能包括:
-
屏幕滑动,其中父 Fragment 中的
ViewPager2
管理一系列子 Fragment 视图。 - 一组相关屏幕中的子导航。
- Jetpack Navigation 将子 Fragment 用作各个目的地。一个 Activity 托管一个父
NavHostFragment
,并在用户浏览应用时以不同的子目的地 Fragment 填充它的空间。
使用 FragmentManager
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
可能如下所示:
supportFragmentManager.commit {
replace<ExampleFragment>(R.id.fragment_container)
setReorderingAllowed(true)
addToBackStack("name") // name can be null
}
在本例中,ExampleFragment
会替换当前在由 R.id.fragment_container
ID 标识的布局容器中的 Fragment(如果有)。将 Fragment 的类提供给 replace()
方法可让 FragmentManager
使用其 FragmentFactory
处理实例化。如需了解详情,请参阅提供依赖项。
setReorderingAllowed(true)
可优化事务中涉及的 Fragment 的状态变化,以使动画和过渡正常运行。如需详细了解如何使用动画和过渡进行导航,请参阅 Fragment 事务和使用动画在 Fragment 之间导航。
调用 addToBackStack()
会将事务提交到返回堆栈。用户稍后可以通过按“返回”按钮反转事务并恢复上一个 Fragment。如果您在一个事务中添加或移除了多个 Fragment,弹出返回堆栈时,所有这些操作都会撤消。在 addToBackStack()
调用中提供的可选名称使您能够使用 popBackStack()
弹回到该特定事务。
如果您在执行移除 Fragment 的事务时未调用 addToBackStack()
,则提交事务时会销毁已移除的 Fragment,用户无法返回到该 Fragment。如果您在移除某个 Fragment 时调用了 addToBackStack()
,则该 Fragment 只会 STOPPED
,稍后当用户返回时它会 RESUMED
。请注意,在这种情况下,其视图会被销毁。如需了解详情,请参阅 Fragment 生命周期。
查找现有 Fragment
您可以使用 findFragmentById()
获取对布局容器中当前 Fragment 的引用。从 XML 扩充时,可使用 findFragmentById()
按给定的 ID 查找 Fragment;在 FragmentTransaction
中添加时,可使用它按容器 ID 进行查找。示例如下:
supportFragmentManager.commit {
replace<ExampleFragment>(R.id.fragment_container)
setReorderingAllowed(true)
addToBackStack(null)
}
...
val fragment: ExampleFragment =
supportFragmentManager.findFragmentById(R.id.fragment_container) as ExampleFragment
或者,您也可以为 Fragment 分配一个唯一的标记,并使用 findFragmentByTag()
获取引用。您可以在布局中定义的 Fragment 上使用 android:tag
XML 属性来分配标记,也可以在 FragmentTransaction
中的 add()
或 replace()
操作期间分配标记。
supportFragmentManager.commit {
replace<ExampleFragment>(R.id.fragment_container, "tag")
setReorderingAllowed(true)
addToBackStack(null)
}
...
val fragment: ExampleFragment =
supportFragmentManager.findFragmentByTag("tag") as ExampleFragment
有关子 Fragment 和同级 Fragment 的特殊注意事项
在任何给定的时间,只允许一个 FragmentManager
控制 Fragment 返回堆栈。如果应用在屏幕上同时显示多个同级 Fragment,或者应用使用子 Fragment,则必须指定一个 FragmentManager
来处理应用的主要导航。
如需在 Fragment 事务内定义主要导航 Fragment,请对事务调用 setPrimaryNavigationFragment()
方法,并传入一个 Fragment 的实例,该 Fragment 的 childFragmentManager
应具有主要控制权。
将导航结构视为一系列层,其中 Activity 作为最外层,封装下面的每一层子 Fragment。每一层都必须有一个主要导航 Fragment。当发生返回事件时,最内层控制导航行为。一旦最内层再也没有可从其弹回的 Fragment 事务,控制权就会回到外面的下一层,此过程会一直重复,直至到达 Activity 为止。
请注意,当同时显示两个或更多 Fragment 时,其中只有一个可以是主要导航 Fragment。如果将某个 Fragment 设为主要导航 Fragment,会移除对先前 Fragment 的指定。在上例中,如果您将详情 fragment 设为主要导航 fragment,就会移除对主 fragment 的指定。