Android Kotlin 协程在使用变量打开的片段上创建 RecyclerView

Android Kotlin coroutines create RecyclerView on fragment open with variable

正如标题中所述,我尝试在使用变量打开的片段上创建一个回收视图。

这是一个没有变量的工作版本:
片段:

viewModel.lists.observe(viewLifecycleOwner) {
   listAdapter.submitList(it)
}

ViewHolder:

val lists = shishaDao.getList(HARDCODED_INT).asLiveData()

如您所见,有一个硬编码的整数。这个整数可以包含不同的值,这正在改变列表值。

这是我对变量的尝试:
片段:

viewModel.lists(title).observe(viewLifecycleOwner) {
   listAdapter.submitList(it)
}

我现在想访问一个需要必要变量的函数,而不是访问 viewholder 的变量。

ViewHolder:

fun lists(title: String): LiveData<List<Tabak>> {
   val nr = dao.getNr(title)
   return dao.getList(nr).asLiveData()
}

应用程序崩溃并出现以下错误:

Cannot access database on the main thread since it may potentially lock the UI for a long period of time.

这里也有试过的方法:

fun lists(title: String): LiveData<List<Tabak>> {
   val nr: Int = 0
   viewModelScope.launch {
      nr = dao.getNr(title)
      Log.e("NR", nr.toString())
   }
   return dao.getList(nr).asLiveData()
}
fun lists(title: String): LiveData<List<Tabak>> {
   val nr: MutableLiveData<Int> = MutableLiveData(0)
   viewModelScope.launch {
      nr.value = dao.getNr(title)
      Log.e("NR", nr.value.toString())
   }
   return dao.getList(nr.value!!).asLiveData()
}

两种方法都不会崩溃。 Log.e 显示正确的数字,但最后一行仍然使用 0.

我的实际问题:如何从 dao.getNr(title) 获取 nr 值以在最后一行 getList(nr) 中使用它?

我找到了一种使用 LiveData 的方法。这是一个很大的方法,但也很有用。

PreferenceManager.kt

class PreferencesManager @Inject constructor(context: Context) {

    private val dataStore = context.createDataStore("user_preferences")

    val preferencesFlow = dataStore.data
        .catch { exception ->
            if (exception is IOException) {
                Log.e(TAG, "Error reading preferences", exception)
                emit(emptyPreferences())
            } else {
                throw exception
            }
        }
        .map { preferences ->
            val choseMarke = preferences[PreferencesKeys.CHOSE_TITLE] ?: 2
            FilterPreference(choseTitle)
        }

    suspend fun updateChoseTitle (choseTitle: Int) {
        dataStore.edit { preferences ->
            preferences[PreferencesKeys.CHOSE_TITLE] = choseTitle
        }
    }

    private object PreferencesKeys {
        val CHOSE_TITLE = preferencesKey<Int>("chosen_title")
    }
}

Fragment.kt 看起来又像这样

viewModel.lists.observe(viewLifecycleOwner) {
   listAdapter.submitList(it)
}

还有 ViewModel.kt

class ListsViewModel @ViewModelInject constructor(private val dao: Dao, private val preferencesManager: PreferencesManager, @Assisted private val state: SavedStateHandle) : ViewModel() {
    val searchQuery = state.getLiveData("searchTitle", "")
    val preferencesFlow = preferencesManager.preferencesFlow

    ...

    private val listsFlow = combine(searchQuery.asFlow(), preferencesFlow) { query, filterPreference ->
        Pair(query, filterPreference)
    }.flatMapLatest { (_, filterPreference) ->
        dao.getList(filterPreference.choseMarke)
    }

我不再尝试在片段中使用标题。在打开片段之前,我需要按下这个标题的按钮,然后我已经将标题(作为来自 dao getNr 的 int)保存在 mainActivity 中。

MainActivity.kt

onNavigationItemSelected(item: MenuItem): Boolean {
   viewModel.onNavigationClicked(item)
}

主要ViewModel.kt

fun onNavigationClicked(item: MenuItem) = viewModelScope.launch {
   val choseMarkeNr = shishaDao.getMarkeNr(item.title.toString())
   preferencesManager.updateChoseMarke(choseMarkeNr)
}

这种方式行得通。 :)