Fragment的生命周期与使用
一、Fragment基础概念
1.1 什么是Fragment
Fragment是Android中的UI组件,表示Activity中的一个行为或用户界面部分。Fragment有自己的生命周期,接收自己的输入事件,可以在Activity运行时添加或删除。Fragment必须始终嵌入在Activity中,其生命周期直接受宿主Activity生命周期的影响。
1.2 Fragment的优势
- 重用性:可以在多个Activity中重用同一个Fragment
- 适配性:便于为不同屏幕尺寸设计不同布局
- 灵活性:可以动态添加、替换和移除
- 模块化:将复杂界面分解为多个Fragment,便于管理
1.3 Fragment与Activity的关系
- Fragment依赖于Activity,不能独立存在
- 一个Activity可以包含多个Fragment
- Fragment可以在Activity运行时动态添加和移除
- Fragment的生命周期受宿主Activity生命周期的影响
二、Fragment生命周期详解
2.1 生命周期方法
class MyFragment : Fragment() {
// 当Fragment与Activity关联时调用
override fun onAttach(context: Context) {
super.onAttach(context)
Log.d("Fragment", "onAttach")
}
// 创建Fragment实例时调用
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")
return inflater.inflate(R.layout.fragment_my, container, false)
}
// 在onCreateView之后调用,可以进行视图初始化
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.d("Fragment", "onViewCreated")
}
// 当Fragment可见时调用
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")
}
// 当Fragment实例被销毁时调用
override fun onDestroy() {
super.onDestroy()
Log.d("Fragment", "onDestroy")
}
// 当Fragment与Activity解除关联时调用
override fun onDetach() {
super.onDetach()
Log.d("Fragment", "onDetach")
}
}
2.2 生命周期流程图
以下是Fragment生命周期的流程:
-
添加Fragment:
onAttach() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume() -
回退栈中弹出Fragment:
onPause() → onStop() → onDestroyView() → onDestroy() → onDetach() -
Fragment被替换并加入回退栈:
onPause() → onStop() → onDestroyView() -
从回退栈恢复Fragment:
onCreateView() → onViewCreated() → onStart() → onResume() -
屏幕旋转等配置变化:
onPause() → onStop() → onDestroyView() → onDestroy() → onDetach() → onAttach() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume()
2.3 Fragment与Activity生命周期的关系
Activity生命周期 | Fragment生命周期 |
---|---|
onCreate() | onAttach() → onCreate() |
onStart() | onCreateView() → onViewCreated() → onStart() |
onResume() | onResume() |
onPause() | onPause() |
onStop() | onStop() → onDestroyView() |
onDestroy() | onDestroy() → onDetach() |
三、Fragment的使用方式
3.1 静态使用Fragment
在布局文件中直接声明Fragment:
<!-- 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">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fragment_container"
android:name="com.example.app.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
3.2 动态使用Fragment
在代码中动态添加、替换或移除Fragment:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
if (savedInstanceState == null) {
// 创建Fragment实例
val fragment = MyFragment()
// 获取FragmentManager
val fragmentManager = supportFragmentManager
// 开始事务
val transaction = fragmentManager.beginTransaction()
// 将Fragment添加到容器中
transaction.add(R.id.fragment_container, fragment)
// 提交事务
transaction.commit()
}
}
// 替换Fragment的方法
private fun replaceFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction()
.replace(R.id.fragment_container, fragment)
.addToBackStack(null) // 添加到回退栈,按返回键可以回到上一个Fragment
.commit()
}
}
3.3 Fragment间通信
3.3.1 通过Activity传递数据
// Fragment向Activity传递数据
interface OnFragmentInteractionListener {
fun onFragmentInteraction(data: String)
}
class MyFragment : 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 sendDataToActivity() {
listener?.onFragmentInteraction("Hello from Fragment")
}
override fun onDetach() {
super.onDetach()
listener = null
}
}
// Activity实现接口
class MainActivity : AppCompatActivity(), OnFragmentInteractionListener {
override fun onFragmentInteraction(data: String) {
// 处理从Fragment接收的数据
Toast.makeText(this, data, Toast.LENGTH_SHORT).show()
// 可以将数据传递给其他Fragment
val otherFragment = supportFragmentManager.findFragmentById(R.id.other_fragment) as? OtherFragment
otherFragment?.receiveData(data)
}
}
3.3.2 使用ViewModel共享数据
// 共享ViewModel
class SharedViewModel : ViewModel() {
val message = MutableLiveData<String>()
fun setMessage(msg: String) {
message.value = msg
}
}
// 第一个Fragment
class FirstFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
}
private fun sendMessage() {
viewModel.setMessage("Message from FirstFragment")
}
}
// 第二个Fragment
class SecondFragment : Fragment() {
private lateinit var viewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
viewModel = ViewModelProvider(requireActivity()).get(SharedViewModel::class.java)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 观察数据变化
viewModel.message.observe(viewLifecycleOwner) { message ->
// 更新UI
textView.text = message
}
}
}
四、Fragment实战技巧
4.1 Fragment懒加载
class LazyFragment : Fragment() {
private var isViewCreated = false
private var isVisibleToUser = false
private var isDataInitiated = false
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
isViewCreated = true
return inflater.inflate(R.layout.fragment_lazy, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 检查是否应该加载数据
prepareLoadData()
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
this.isVisibleToUser = isVisibleToUser
prepareLoadData()
}
private fun prepareLoadData() {
if (isViewCreated && isVisibleToUser && !isDataInitiated) {
loadData()
isDataInitiated = true
}
}
private fun loadData() {
// 执行数据加载操作
Log.d("LazyFragment", "数据加载开始")
}
override fun onDestroyView() {
super.onDestroyView()
isViewCreated = false
isDataInitiated = false
}
}
4.2 ViewPager2与Fragment结合使用
// 定义适配器
class ViewPagerAdapter(fragmentActivity: FragmentActivity) :
FragmentStateAdapter(fragmentActivity) {
private val fragments = listOf(
FirstFragment(),
SecondFragment(),
ThirdFragment()
)
override fun getItemCount(): Int = fragments.size
override fun createFragment(position: Int): Fragment = fragments[position]
}
// 在Activity中使用
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val viewPager = findViewById<ViewPager2>(R.id.view_pager)
val adapter = ViewPagerAdapter(this)
viewPager.adapter = adapter
// 配合TabLayout使用
val tabLayout = findViewById<TabLayout>(R.id.tab_layout)
TabLayoutMediator(tabLayout, viewPager) { tab, position ->
tab.text = "Tab ${position + 1}"
}.attach()
}
}
4.3 Fragment状态保存与恢复
class StateFragment : Fragment() {
private var counter = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// 恢复状态
savedInstanceState?.let {
counter = it.getInt("counter", 0)
}
}
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
return inflater.inflate(R.layout.fragment_state, container, false)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 更新UI显示计数器值
updateCounterDisplay()
// 设置按钮点击事件
view.findViewById<Button>(R.id.btn_increase).setOnClickListener {
counter++
updateCounterDisplay()
}
}
private fun updateCounterDisplay() {
view?.findViewById<TextView>(R.id.tv_counter)?.text = counter.toString()
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
// 保存状态
outState.putInt("counter", counter)
}
}
五、面试题解析
Q1:Fragment与Activity有什么区别?
答:Fragment与Activity的主要区别:
- Fragment必须嵌入在Activity中,不能独立存在,而Activity可以独立存在
- Fragment有自己的生命周期,但其生命周期受宿主Activity的影响
- 一个Activity可以包含多个Fragment,而Fragment只能附加到一个Activity上
- Fragment更加轻量级,便于重用和组合
- Fragment可以在运行时动态添加、替换和移除,更加灵活
- Fragment引入的目的是为了解决不同屏幕尺寸的适配问题
Q2:Fragment的生命周期与Activity的生命周期有什么关联?
答:Fragment的生命周期与Activity的生命周期关联:
- 当Activity创建时,其中的Fragment会依次经历onAttach()、onCreate()等生命周期方法
- 当Activity暂停时,其中所有的Fragment都会先暂停
- 当Activity销毁时,其中所有的Fragment都会被销毁
- Fragment的生命周期方法比Activity更细化,如onCreateView()、onViewCreated()等
- 当Fragment被添加到回退栈时,只会调用到onDestroyView(),而不会调用onDestroy()和onDetach()
Q3:如何处理Fragment重叠问题?
答:处理Fragment重叠问题的方法:
- 检查FragmentTransaction的使用,确保使用replace()而不是add()方法(除非明确需要多个Fragment叠加)
- 在Activity重建时(如屏幕旋转)检查savedInstanceState,避免重复添加Fragment
- 为Fragment设置唯一的tag或id,便于管理和查找
- 使用FragmentManager的findFragmentById()或findFragmentByTag()方法检查Fragment是否已存在
- 在处理回退栈时,注意Fragment的添加和移除顺序
- 使用hide()和show()方法代替add()和remove(),可以更好地控制Fragment的可见性
六、开源项目实战
6.1 使用Navigation组件实现Fragment导航
Jetpack Navigation组件提供了一种现代化的Fragment导航方案:
<!-- nav_graph.xml -->
<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.app.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.app.DetailFragment"
android:label="详情页">
<argument
android:name="itemId"
app:argType="integer" />
</fragment>
</navigation>
// 在Activity中设置NavController
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val navHostFragment = supportFragmentManager
.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navController = navHostFragment.navController
// 设置ActionBar与NavController关联
setupActionBarWithNavController(navController)
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment)
return navController.navigateUp() || super.onSupportNavigateUp()
}
}
// 在Fragment中进行导航
class HomeFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
view.findViewById<Button>(R.id.btn_to_detail).setOnClickListener {
// 带参数导航到详情页
val action = HomeFragmentDirections.actionHomeToDetail(itemId = 1)
findNavController().navigate(action)
}
}
}
// 在目标Fragment中接收参数
class DetailFragment : Fragment() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 获取传递的参数
val args = DetailFragmentArgs.fromBundle(requireArguments())
val itemId = args.itemId
// 使用参数更新UI
view.findViewById<TextView>(R.id.tv_item_id).text = "Item ID: $itemId"
}
}
项目地址:Navigation
6.2 使用Dagger Hilt实现Fragment依赖注入
Dagger Hilt是Android官方推荐的依赖注入框架,可以简化Fragment中的依赖注入:
// 在Application中启用Hilt
@HiltAndroidApp
class MyApplication : Application()
// 定义ViewModel
@HiltViewModel
class MyViewModel @Inject constructor(
private val repository: MyRepository
) : ViewModel() {
// ViewModel实现
}
// 在Fragment中使用
@AndroidEntryPoint
class MyFragment : Fragment() {
// 注入ViewModel
private val viewModel: MyViewModel by viewModels()
// 直接注入依赖
@Inject
lateinit var analytics: AnalyticsService
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
// 使用注入的依赖
analytics.logEvent("fragment_viewed")
// 使用ViewModel
viewModel.data.observe(viewLifecycleOwner) { data ->
// 更新UI
}
}
}
项目地址:Dagger Hilt
总结
本文详细介绍了Fragment的生命周期和使用方法,从基础概念到高级应用,系统地讲解了如何在Android应用中有效地使用Fragment。Fragment作为Android UI开发中的重要组件,为构建灵活、可重用的用户界面提供了强大支持。
在实际开发中,应深入理解Fragment的生命周期,合理处理Fragment间的通信,并结合现代化的组件如Navigation、ViewModel等,构建更加健壮和易维护的应用。同时,掌握Fragment的各种优化技巧,如懒加载、状态保存等,可以进一步提升应用的性能和用户体验。
下一篇文章将介绍Material Design规范应用,敬请期待!