Fragment 组件
Fragment 是 Android 中用于构建灵活界面的组件,它可以嵌入在 Activity 中,实现界面的模块化和复用。现代 Android 开发推荐使用单 Activity 架构,配合 Fragment 和 Navigation 组件构建应用。
什么是 Fragment?
Fragment 代表 Activity 中的一部分界面或行为,它有自己的布局和生命周期,但必须依附于 Activity 存在。Fragment 的主要优势:
- 模块化:将界面拆分为独立的模块,便于维护
- 复用性:同一个 Fragment 可以在多个 Activity 中使用
- 灵活性:可以动态添加、移除、替换 Fragment
- 适配性:针对不同屏幕尺寸设计不同的布局组合
创建 Fragment
1. 创建 Fragment 类
class HomeFragment : Fragment() {
private var _binding: FragmentHomeBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentHomeBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 初始化视图
binding.tvTitle.text = "首页"
binding.btnAction.setOnClickListener {
// 处理点击
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}
2. 创建布局文件
<!-- res/layout/fragment_home.xml -->
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:id="@+id/tv_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="首页"
android:textSize="24sp" />
<Button
android:id="@+id/btn_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="点击"
android:layout_marginTop="16dp" />
</LinearLayout>
Fragment 生命周期
Fragment 拥有独立的生命周期,与 Activity 生命周期密切相关。
生命周期回调方法
class HomeFragment : Fragment() {
override fun onAttach(context: Context) {
super.onAttach(context)
Log.d("Fragment", "onAttach: Fragment 附加到 Activity")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d("Fragment", "onCreate: Fragment 创建")
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
Log.d("Fragment", "onCreateView: 创建 Fragment 视图")
return super.onCreateView(inflater, container, savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("Fragment", "onViewCreated: 视图创建完成")
}
override fun onStart() {
super.onStart()
Log.d("Fragment", "onStart: Fragment 可见")
}
override fun onResume() {
super.onResume()
Log.d("Fragment", "onResume: Fragment 获得焦点")
}
override fun onPause() {
super.onPause()
Log.d("Fragment", "onPause: Fragment 失去焦点")
}
override fun onStop() {
super.onStop()
Log.d("Fragment", "onStop: Fragment 不可见")
}
override fun onDestroyView() {
super.onDestroyView()
Log.d("Fragment", "onDestroyView: 视图销毁")
}
override fun onDestroy() {
super.onDestroy()
Log.d("Fragment", "onDestroy: Fragment 销毁")
}
override fun onDetach() {
super.onDetach()
Log.d("Fragment", "onDetach: Fragment 从 Activity 分离")
}
}
与 Activity 生命周期的关系
| Activity 状态 | Fragment 回调 |
|---|---|
| onCreate | onAttach → onCreate → onCreateView → onViewCreated |
| onStart | onStart |
| onResume | onResume |
| onPause | onPause |
| onStop | onStop |
| onDestroy | onDestroyView → onDestroy → onDetach |
在 Activity 中使用 Fragment
静态添加
在布局文件中直接声明 Fragment:
<!-- res/layout/activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="@+id/fragment_home"
android:name="com.example.myapp.HomeFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
动态添加(推荐)
使用 FragmentManager 在代码中动态管理 Fragment:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// 检查是否已保存状态,避免重复添加
if (savedInstanceState == null) {
// 创建 Fragment 实例
val fragment = HomeFragment()
// 获取 FragmentManager 并开始事务
supportFragmentManager.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit()
}
}
}
Fragment 事务
Fragment 事务用于执行 Fragment 的添加、移除、替换等操作:
val fragmentManager = supportFragmentManager
// 添加 Fragment
fragmentManager.beginTransaction()
.add(R.id.fragment_container, HomeFragment())
.commit()
// 替换 Fragment
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, DetailFragment())
.commit()
// 移除 Fragment
fragmentManager.beginTransaction()
.remove(fragment)
.commit()
// 隐藏/显示 Fragment
fragmentManager.beginTransaction()
.hide(fragment)
.commit()
fragmentManager.beginTransaction()
.show(fragment)
.commit()
添加到返回栈
将 Fragment 添加到返回栈,支持返回键导航:
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, DetailFragment())
.addToBackStack(null) // 添加到返回栈
.commit()
事务优化
// 使用 commitAllowingStateLoss 避免状态丢失异常
fragmentManager.beginTransaction()
.replace(R.id.fragment_container, DetailFragment())
.commitAllowingStateLoss()
// 设置过渡动画
fragmentManager.beginTransaction()
.setCustomAnimations(
R.anim.slide_in_right, // 进入动画
R.anim.slide_out_left, // 退出动画
R.anim.slide_in_left, // 弹出时进入动画
R.anim.slide_out_right // 弹出时退出动画
)
.replace(R.id.fragment_container, DetailFragment())
.addToBackStack(null)
.commit()
Fragment 间通信
Fragment 与 Activity 通信
通过接口回调:
// 定义接口
interface OnFragmentInteractionListener {
fun onItemSelected(itemId: Int)
}
class ListFragment : Fragment() {
private var listener: OnFragmentInteractionListener? = null
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is OnFragmentInteractionListener) {
listener = context
} else {
throw RuntimeException("$context must implement OnFragmentInteractionListener")
}
}
private fun onItemClicked(id: Int) {
listener?.onItemSelected(id)
}
override fun onDetach() {
super.onDetach()
listener = null
}
}
// Activity 实现接口
class MainActivity : AppCompatActivity(), OnFragmentInteractionListener {
override fun onItemSelected(itemId: Int) {
// 处理 Fragment 的回调
}
}
通过 ViewModel 共享数据(推荐):
class SharedViewModel : ViewModel() {
private val _selectedItem = MutableLiveData<Int>()
val selectedItem: LiveData<Int> = _selectedItem
fun selectItem(id: Int) {
_selectedItem.value = id
}
}
class ListFragment : Fragment() {
private val viewModel: SharedViewModel by activityViewModels()
private fun onItemClicked(id: Int) {
viewModel.selectItem(id)
}
}
class DetailFragment : Fragment() {
private val viewModel: SharedViewModel by activityViewModels()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
viewModel.selectedItem.observe(viewLifecycleOwner) { id ->
// 更新详情
}
}
}
Fragment 之间通信
通过共享 ViewModel 实现 Fragment 之间的通信:
// 两个 Fragment 共享同一个 Activity 的 ViewModel
class FragmentA : Fragment() {
private val viewModel: SharedViewModel by activityViewModels()
}
class FragmentB : Fragment() {
private val viewModel: SharedViewModel by activityViewModels()
}
Fragment 参数传递
使用 Bundle
class DetailFragment : Fragment() {
companion object {
private const val ARG_ITEM_ID = "item_id"
fun newInstance(itemId: Int): DetailFragment {
return DetailFragment().apply {
arguments = Bundle().apply {
putInt(ARG_ITEM_ID, itemId)
}
}
}
}
private var itemId: Int = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
arguments?.let {
itemId = it.getInt(ARG_ITEM_ID)
}
}
}
// 使用
val fragment = DetailFragment.newInstance(123)
使用 Fragment Result API
// 在目标 Fragment 设置结果
class DetailFragment : Fragment() {
private fun returnResult() {
val result = "操作成功"
parentFragmentManager.setFragmentResult(
"requestKey",
bundleOf("result" to result)
)
}
}
// 在源 Fragment 监听结果
class ListFragment : Fragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
parentFragmentManager.setFragmentResultListener(
"requestKey",
this
) { requestKey, bundle ->
val result = bundle.getString("result")
// 处理结果
}
}
}
Navigation 组件
Navigation 组件是 Android Jetpack 的一部分,用于简化 Fragment 导航。
添加依赖
dependencies {
implementation("androidx.navigation:navigation-fragment-ktx:2.7.6")
implementation("androidx.navigation:navigation-ui-ktx:2.7.6")
}
创建导航图
在 res/navigation/ 目录下创建 nav_graph.xml:
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/nav_graph"
app:startDestination="@id/homeFragment">
<fragment
android:id="@+id/homeFragment"
android:name="com.example.myapp.HomeFragment"
android:label="首页">
<action
android:id="@+id/action_home_to_detail"
app:destination="@id/detailFragment" />
</fragment>
<fragment
android:id="@+id/detailFragment"
android:name="com.example.myapp.DetailFragment"
android:label="详情">
<argument
android:name="itemId"
app:argType="integer" />
</fragment>
</navigation>
在 Activity 中使用
<!-- activity_main.xml -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:defaultNavHost="true"
app:navGraph="@navigation/nav_graph" />
</LinearLayout>
导航操作
class HomeFragment : Fragment() {
private fun navigateToDetail(itemId: Int) {
// 使用 action 导航
findNavController().navigate(
R.id.action_home_to_detail,
bundleOf("itemId" to itemId)
)
// 或使用 Safe Args(推荐)
val direction = HomeFragmentDirections.actionHomeToDetail(itemId)
findNavController().navigate(direction)
}
private fun goBack() {
findNavController().navigateUp()
}
}
底部导航
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
// 绑定底部导航
binding.bottomNav.setupWithNavController(navController)
}
}
小结
本章介绍了 Fragment 组件的核心内容:
- Fragment 基础:创建 Fragment 类和布局
- 生命周期:理解 Fragment 的生命周期及与 Activity 的关系
- Fragment 管理:静态添加和动态管理 Fragment
- Fragment 通信:接口回调、ViewModel、Fragment Result API
- 参数传递:Bundle 和 Safe Args
- Navigation 组件:简化 Fragment 导航
下一章将学习 Android 的数据存储方案。