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)
}
这种方式行得通。 :)
正如标题中所述,我尝试在使用变量打开的片段上创建一个回收视图。
这是一个没有变量的工作版本:
片段:
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)
}
这种方式行得通。 :)