跳到主要内容

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 回调
onCreateonAttach → onCreate → onCreateView → onViewCreated
onStartonStart
onResumeonResume
onPauseonPause
onStoponStop
onDestroyonDestroyView → 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 组件是 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 组件的核心内容:

  1. Fragment 基础:创建 Fragment 类和布局
  2. 生命周期:理解 Fragment 的生命周期及与 Activity 的关系
  3. Fragment 管理:静态添加和动态管理 Fragment
  4. Fragment 通信:接口回调、ViewModel、Fragment Result API
  5. 参数传递:Bundle 和 Safe Args
  6. Navigation 组件:简化 Fragment 导航

下一章将学习 Android 的数据存储方案。