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

如何借助ViewModel在设备旋转时保存并恢复Spinner选中状态?

解决Spinner重建时选中状态被覆盖的问题

问题根源很明确:Fragment重建后,要么是设置Adapter时Spinner自动选中第0项并触发监听器,要么是你先绑定了监听器再恢复选中状态,导致代码触发的选中事件也被监听器捕获,直接覆盖了ViewModel里存的历史选中索引。

下面给你两个简单可行的解决办法:

办法一:用标志位区分手动选择和代码触发

在Fragment里加个布尔标志,只有用户手动点选Spinner的时候,才更新ViewModel的状态:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    var isRestoringState = false

    // 设置Spinner选中监听器
    binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
        override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
            // 只有非状态恢复阶段,才更新ViewModel
            if (!isRestoringState) {
                viewModel.setSelectedItem(p2)
            }
            // 用完就把标志位重置
            isRestoringState = false
        }

        override fun onNothingSelected(p0: AdapterView<*>?) {
            // 按需实现即可
        }
    }

    // 先观察数据,设置完Adapter再恢复选中状态
    viewModel.spinnerData.observe(this) { spinnerArray ->
        ArrayAdapter(
            requireContext(),
            R.layout.simple_spinner_dropdown_item,
            spinnerArray
        ).also { adapter ->
            binding.spinner.adapter = adapter
            // 标记当前是恢复状态,避免监听器误触发更新
            isRestoringState = true
            binding.spinner.setSelection(viewModel.getSelectedCompanyIndex())
        }
    }
}

办法二:调整顺序,先设置Adapter和恢复状态,再绑监听器

等Spinner有了数据、并且已经恢复到历史选中项之后,再绑定监听器,这样代码触发的选中事件就不会被监听器捕获:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)
    
    // 先监听Spinner数据变化
    viewModel.spinnerData.observe(this) { spinnerArray ->
        ArrayAdapter(
            requireContext(),
            R.layout.simple_spinner_dropdown_item,
            spinnerArray
        ).also { adapter ->
            binding.spinner.adapter = adapter
            // 先把Spinner恢复到之前选中的位置
            binding.spinner.setSelection(viewModel.getSelectedCompanyIndex())
            // 最后再绑定监听器,只处理用户手动操作
            binding.spinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
                override fun onItemSelected(p0: AdapterView<*>?, p1: View?, p2: Int, p3: Long) {
                    viewModel.setSelectedItem(p2)
                }

                override fun onNothingSelected(p0: AdapterView<*>?) {
                    // 按需实现即可
                }
            }
        }
    }
}

两个关键提醒

  1. 一定要先设置Adapter,再调用setSelection,不然Spinner没有数据项,恢复选中状态的操作会无效。
  2. 核心思路就是把代码触发的选中事件和用户手动操作的事件区分开,避免意外覆盖ViewModel的状态。

内容的提问来源于stack exchange,提问作者yambo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.07 03:40:44