Android UI开发基础:Fragment的生命周期与使用

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生命周期的流程:

  1. 添加Fragment
    onAttach() → onCreate() → onCreateView() → onViewCreated() → onStart() → onResume()

  2. 回退栈中弹出Fragment
    onPause() → onStop() → onDestroyView() → onDestroy() → onDetach()

  3. Fragment被替换并加入回退栈
    onPause() → onStop() → onDestroyView()

  4. 从回退栈恢复Fragment
    onCreateView() → onViewCreated() → onStart() → onResume()

  5. 屏幕旋转等配置变化
    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的主要区别:

  1. Fragment必须嵌入在Activity中,不能独立存在,而Activity可以独立存在
  2. Fragment有自己的生命周期,但其生命周期受宿主Activity的影响
  3. 一个Activity可以包含多个Fragment,而Fragment只能附加到一个Activity上
  4. Fragment更加轻量级,便于重用和组合
  5. Fragment可以在运行时动态添加、替换和移除,更加灵活
  6. Fragment引入的目的是为了解决不同屏幕尺寸的适配问题

Q2:Fragment的生命周期与Activity的生命周期有什么关联?

答:Fragment的生命周期与Activity的生命周期关联:

  1. 当Activity创建时,其中的Fragment会依次经历onAttach()、onCreate()等生命周期方法
  2. 当Activity暂停时,其中所有的Fragment都会先暂停
  3. 当Activity销毁时,其中所有的Fragment都会被销毁
  4. Fragment的生命周期方法比Activity更细化,如onCreateView()、onViewCreated()等
  5. 当Fragment被添加到回退栈时,只会调用到onDestroyView(),而不会调用onDestroy()和onDetach()

Q3:如何处理Fragment重叠问题?

答:处理Fragment重叠问题的方法:

  1. 检查FragmentTransaction的使用,确保使用replace()而不是add()方法(除非明确需要多个Fragment叠加)
  2. 在Activity重建时(如屏幕旋转)检查savedInstanceState,避免重复添加Fragment
  3. 为Fragment设置唯一的tag或id,便于管理和查找
  4. 使用FragmentManager的findFragmentById()或findFragmentByTag()方法检查Fragment是否已存在
  5. 在处理回退栈时,注意Fragment的添加和移除顺序
  6. 使用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规范应用,敬请期待!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

键盘小码哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值