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

Android Kotlin:协程加载数据完成后如何更新Spinner UI?

解决协程加载数据后更新Spinner的问题

你的核心问题是ViewModel中的数据更新无法通知到UI层——你用了普通的HashMap变量,UI层只能获取到初始空值,协程加载完成后数据变化不会触发UI更新。下面是具体解决方法:

1. 改造ViewModel:使用可观察的状态容器

把普通的langToCode和codeToLang换成StateFlow(Jetpack官方推荐的协程状态容器),让UI层能感知数据变化:

class TranslateViewModel : ViewModel() {
    // 用MutableStateFlow存储可变状态,对外暴露不可变的StateFlow
    private val _langToCode = MutableStateFlow<Map<String, String>>(emptyMap())
    val langToCode: StateFlow<Map<String, String>> = _langToCode

    private val _codeToLang = MutableStateFlow<Map<String, String>>(emptyMap())
    val codeToLang: StateFlow<Map<String, String>> = _codeToLang

    private val translateRepository = TranslateRepository()

    init {
        viewModelScope.launch {
            val langList = try {
                translateRepository.getLangList()
            } catch (e: IOException) {
                Log.e(TAG, "IOException", e)
                return@launch
            } catch (e: HttpException) {
                Log.e(TAG, "HTTPException", e)
                return@launch
            }
            if (langList.isSuccessful && langList.body() != null) {
                val fetchedCodeToLang = langList.body()!!.langList
                _codeToLang.value = fetchedCodeToLang
                // 转换映射,无需强制转HashMap,Map类型足够使用
                val fetchedLangToCode = fetchedCodeToLang.map { (key, value) -> value to key }.toMap()
                _langToCode.value = fetchedLangToCode
            } else {
                Log.d(TAG, "Get lang list failure")
            }
        }
    }

    companion object {
        private const val TAG = "TranslateViewModel"
    }
}

2. UI层观察状态变化,更新Spinner

在onViewCreated中,用lifecycleScope观察langToCode的变化,每当数据更新时重新设置Spinner的Adapter:

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
    super.onViewCreated(view, savedInstanceState)

    viewBinding.apply {
        // 用lifecycleScope观察StateFlow,确保仅在Fragment活跃时接收数据
        lifecycleScope.launchWhenStarted {
            viewModel.langToCode.collect { langMap ->
                val langList = langMap.keys.toList()
                val arrayAdapter = ArrayAdapter(
                    requireContext(),
                    android.R.layout.simple_spinner_item,
                    langList
                ).also {
                    it.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
                }
                sourceLanguage.adapter = arrayAdapter
            }
        }
    }
}

方案说明

  • StateFlow是可观察数据流,当ViewModel中的_langToCode.value更新时,所有订阅它的UI层都会收到新数据。
  • launchWhenStarted确保协程仅在Fragment处于活跃状态时运行,避免内存泄漏,同时Fragment从后台回到前台时会自动重新收集数据。

如果你更习惯使用LiveData,也可以替换为MutableLiveData实现:
ViewModel中:

private val _langToCode = MutableLiveData<Map<String, String>>(emptyMap())
val langToCode: LiveData<Map<String, String>> = _langToCode

UI层中:

viewModel.langToCode.observe(viewLifecycleOwner) { langMap ->
    // 复用上述更新Spinner的逻辑
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.20 01:58:12