You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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("&#8226;", 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 23:31:46