如何在Kotlin for Android中在协程完成后更新我的用户界面?

huangapple go评论59阅读模式
英文:

How can I update my UI after a coroutine is completed in Kotlin for Android?

问题

我使用Retrofit和协程从API获取语言列表。我的ViewModel在创建时加载语言列表。我想将此列表加载到一个下拉框中,但每当我更新下拉框的数据时,ViewModel中的数据还没有完全加载。

以下是我的ViewModel类,用于从API加载数据:

class TranslateViewModel : ViewModel() {
    var langToCode = HashMap<String, String>()
    var codeToLang = HashMap<String, String>()
    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) {
                codeToLang = langList.body()!!.langList
                langToCode = codeToLang.map { (key, value) ->
                    value to key
                }.toMap() as HashMap<String, String>
            } else {
                Log.d(TAG, "Get lang list failure")
            }
        }
    }
}

我想将语言列表加载到下拉框中,但是我的ViewModel加载数据的速度太慢了,所以langList变量始终为空。

以下是如何在协程完成加载数据后立即更新UI的方法:

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

    viewBinding.apply {
        sourceLanguage.apply {
            viewModel.viewModelScope.launch {
                val langList = viewModel.langToCode.keys.toList()
                val arrayAdapter = ArrayAdapter(
                    context,
                    android.R.layout.simple_spinner_item,
                    langList
                ).also {
                    it.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
                }
                adapter = arrayAdapter
            }
        }
    }
}

感谢您的帮助,请原谅我英语不太好的问题。

英文:

I use Retrofit and Coroutines to fetch a list of languages from an API. My ViewModel loads the list of languages whenever it is created. I want to load this list into a spinner, but whenever I update the spinner data, the data in the ViewModel hasn't completely loaded.

Here my viewmodel class to load data from an api

class TranslateViewModel : ViewModel() {
    var langToCode = HashMap&lt;String, String&gt;()
    var codeToLang = HashMap&lt;String, String&gt;()
    private val translateRepository = TranslateRepository()

    init {
        viewModelScope.launch {
            val langList = try {
                translateRepository.getLangList()
            } catch (e: IOException) {
                Log.e(TAG, &quot;IOException&quot;, e)
                return@launch
            } catch (e: HttpException) {
                Log.e(TAG, &quot;HTTPException&quot;, e)
                return@launch
            }
            if (langList.isSuccessful &amp;&amp; langList.body() != null) {
                codeToLang = langList.body()!!.langList
                langToCode = codeToLang.map { (key, value) -&gt;
                    value to key
                }.toMap() as HashMap&lt;String, String&gt;
            } else {
                Log.d(TAG, &quot;Get lang list failure&quot;)
            }
        }
    }
}

I want to load my list of languages into my spinner but my viewmodel is taking too long to load the data, so the langList variable is alway empty.

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

        viewBinding.apply {
            sourceLanguage.apply {
                val langList = viewModel.langToCode.keys.toList()
                val arrayAdapter = ArrayAdapter (
                    context,
                    android.R.layout.simple_spinner_item,
                    langList
                ).also { it.setDropDownViewResource(
                     android.R.layout.simple_spinner_dropdown_item
                )
                }
                this.adapter = arrayAdapter
        }
}

I want to know how to update my UI immediately after my coroutine has finished loading data. I appreciate all of your help, and please forgive me for my poor English skills.

答案1

得分: 0

在您的情况下,更新您的ViewModel如下所示-

class TranslateViewModel : ViewModel() {
    private val _langList = MutableLiveData<List<String>>()
    val langList: LiveData<List<String>> get() = _langList

    init {
        viewModelScope.launch {
            if (langList.isSuccessful && langList.body() != null) {
                codeToLang = langList.body()!!.langList
                langToCode = codeToLang.map { (key, value) ->
                    value to key
                }.toMap() as HashMap<String, String>
                _langList.value = langToCode.keys.toList() // 更新LiveData的值
            } else {
                Log.d(TAG, "获取语言列表失败")
            }
        }
    }
}

在您的UI代码中,观察LiveData并按如下方式更新Spinner-

viewModel.langList.observe(viewLifecycleOwner, { langList ->
    val arrayAdapter = ArrayAdapter(
        context,
        android.R.layout.simple_spinner_item,
        langList
    ).also { it.setDropDownViewResource(
         android.R.layout.simple_spinner_dropdown_item
    )
    }
    sourceLanguage.adapter = arrayAdapter
})

点击此链接获取更多详细信息- https://developer.android.com/topic/libraries/architecture/livedata

英文:

In Your case update your viewmodel as below-

class TranslateViewModel : ViewModel() {
    private val _langList = MutableLiveData&lt;List&lt;String&gt;&gt;()
    val langList: LiveData&lt;List&lt;String&gt;&gt; get() = _langList
    
    init {
        viewModelScope.launch {
            if (langList.isSuccessful &amp;&amp; langList.body() != null) {
                codeToLang = langList.body()!!.langList
                langToCode = codeToLang.map { (key, value) -&gt;
                    value to key
                }.toMap() as HashMap&lt;String, String&gt;
                _langList.value = langToCode.keys.toList() // Update the LiveData value
            } else {
                Log.d(TAG, &quot;Get lang list failure&quot;)
            }
        }
    }
}

In your UI code to observe the LiveData and update the spinner as below-

viewModel.langList.observe(viewLifecycleOwner, { langList -&gt;
            val arrayAdapter = ArrayAdapter(
                context,
                android.R.layout.simple_spinner_item,
                langList
            ).also { it.setDropDownViewResource(
                 android.R.layout.simple_spinner_dropdown_item
            )
            }
            sourceLanguage.adapter = arrayAdapter
        })

Follow this link for more detail - https://developer.android.com/topic/libraries/architecture/livedata

huangapple
  • 本文由 发表于 2023年6月2日 02:47:43
  • 转载请务必保留本文链接:https://go.coder-hub.com/76384843.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定