Android Fragment:应在onCreateView还是onViewCreated中填充布局?是否需ViewModel?
引导页Fragment的两个问题解答
一、onCreateView 和 onViewCreated 的分工
你当前的写法完全正确,这也是官方推荐的Fragment生命周期职责划分:
onCreateView只负责加载并返回布局,也就是你写的inflater.inflate(R.layout.fragment_onboarding, container, false)这行代码。不要在这里做任何View初始化或业务逻辑操作,保持职责单一。onViewCreated是在布局加载完成、View实例完全创建后触发的,此时可以安全地通过传入的view参数执行findViewById、设置Adapter、绑定点击监听等操作。你把所有View相关逻辑放在这里,完全符合规范。
如果强行在onCreateView里操作View,不仅会让代码结构混乱,还可能因View未完全初始化引发空指针问题,所以保持当前写法即可。
二、是否需要创建ViewModel?
分两种场景判断:
1. 基础需求(临时凑合用)
如果你的引导页只是固定内容滑动,不在乎屏幕旋转后回到第一页,也不需要记录用户是否已看过引导(实际应用中几乎都需要记录),那可以暂时不用ViewModel。但这种情况下,屏幕旋转后用户需要重新滑动,体验会打折扣。
2. 最佳实践(推荐实现)
想要提升体验、贴合Jetpack架构规范,建议创建ViewModel,原因如下:
- 保存UI状态:ViewModel的生命周期独立于Fragment的配置变更,屏幕旋转后能记住用户滑动到的页面位置,无需重新操作。
- 封装业务逻辑:比如记录用户是否完成引导的逻辑(读写SharedPreferences),可以放到ViewModel中,让Fragment只负责UI展示,职责更清晰。
- 预留扩展性:后续如果引导页内容需要从接口动态获取,ViewModel可以直接处理数据请求,无需修改Fragment的核心逻辑。
代码改造示例(ViewModel版)
ViewModel实现
class OnBoardingViewModel : ViewModel() { // 保存当前ViewPager的滑动位置 val currentPos = MutableLiveData(0) // 标记用户已完成引导页 fun markOnBoardingCompleted(context: Context) { val sharedPref = context.getSharedPreferences("app_settings", Context.MODE_PRIVATE) sharedPref.edit().putBoolean("on_boarding_completed", true).apply() } // 检查用户是否已经看过引导页 fun isOnBoardingCompleted(context: Context): Boolean { val sharedPref = context.getSharedPreferences("app_settings", Context.MODE_PRIVATE) return sharedPref.getBoolean("on_boarding_completed", false) } }
Fragment中使用ViewModel
class OnBoardingFragment : Fragment() { private val viewModel: OnBoardingViewModel by viewModels() private lateinit var viewPager: ViewPager private lateinit var dotsLayout: LinearLayout private lateinit var sliderAdapter: SliderAdapter private lateinit var dots: Array<TextView?> private lateinit var letsGetStarted: Button private lateinit var next: Button private var currentPos: Int = 0 override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View? { return inflater.inflate(R.layout.fragment_onboarding, container, false) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) viewPager = view.findViewById(R.id.slider) dotsLayout = view.findViewById(R.id.dots) letsGetStarted = view.findViewById(R.id.get_started_btn) next = view.findViewById(R.id.next_btn) sliderAdapter = SliderAdapter(requireContext()) viewPager.adapter = sliderAdapter addDots(0) // 监听ViewModel中位置变化,自动更新UI viewModel.currentPos.observe(viewLifecycleOwner) { pos -> addDots(pos) currentPos = pos val animation = AnimationUtils.loadAnimation(requireContext(), android.R.anim.fade_in) if (pos == 0) { letsGetStarted.visibility = View.INVISIBLE next.apply { this.animation = animation visibility = View.VISIBLE } } else { letsGetStarted.apply { this.animation = animation visibility = View.VISIBLE } next.visibility = View.INVISIBLE } } viewPager.addOnPageChangeListener(object : ViewPager.OnPageChangeListener { override fun onPageScrolled( position: Int, positionOffset: Float, positionOffsetPixels: Int ) {} override fun onPageSelected(position: Int) { // 更新ViewModel中的位置 viewModel.currentPos.value = position } override fun onPageScrollStateChanged(state: Int) {} }) next.setOnClickListener { viewPager.currentItem = currentPos + 1 } letsGetStarted.setOnClickListener { viewModel.markOnBoardingCompleted(requireContext()) findNavController().navigate(R.id.action_onBoardingFragment_to_loginFragment) } } private fun addDots(position: Int) { dots = arrayOfNulls(2) dotsLayout.removeAllViews() for (i in dots.indices) { dots[i] = TextView(requireContext()) dots[i]!!.text = HtmlCompat.fromHtml("•", HtmlCompat.FROM_HTML_MODE_LEGACY) dots[i]!!.setTextColor( ContextCompat.getColor( requireContext(), android.R.color.darker_gray ) ) dots[i]!!.textSize = 35F dotsLayout.addView(dots[i]) } if (dots.isNotEmpty()) { dots[position]!!.setTextColor( ContextCompat.getColor( requireContext(), R.color.wine_red ) ) } } }
内容的提问来源于stack exchange,提问作者İbrahim Uyar
相关产品推荐
相关产品推荐

