Jetpack Compose Horizontal Pager列表状态保存及数据更新问题排查
问题原因
- 你在ViewModel中通过
getQuestions监听了数据库的全量查询Flow,只要调用updateKnowAnswer修改了数据库中题目的knowAnswer字段,distinctUntilChanged检测到数据集变更就会推送新的QueryState.Success状态到UI层。 - 你当前在UI层每次收到新的Success状态都会重新执行
shuffled()打乱列表,自然会生成全新的题目序列,HorizontalPager的状态也会被同步重置。
解决方案
方案一:在ViewModel中缓存打乱后的列表(更推荐)
将筛选、打乱逻辑放到ViewModel侧,仅在首次加载、用户主动切换筛选条件时重新生成打乱序列,数据库更新时仅修改列表对应条目的字段,不改变整体顺序。
修改ViewModel代码:
// 缓存当前使用的筛选条件 private var currentFilter: String = "all" // 缓存已打乱的题目列表 private val _cachedShuffledQuestions = MutableStateFlow<List<CertQuestions>>(emptyList()) val cachedShuffledQuestions = _cachedShuffledQuestions.asStateFlow() fun getQuestions() = viewModelScope.launch { try { certsRepository.getAllQuestions().distinctUntilChanged().collect(){ dbResult-> if(dbResult.isEmpty()){ _quState.value = QueryState.Empty _cachedShuffledQuestions.value = emptyList() }else { _quState.value = QueryState.Success(dbResult) // 仅缓存为空或筛选条件变更时重新生成打乱序列 if (_cachedShuffledQuestions.value.isEmpty()) { refreshShuffledList(dbResult) } else { // 数据库更新时仅更新对应条目字段,保留原有顺序 val updatedList = _cachedShuffledQuestions.value.map { cachedQuestion -> dbResult.find { it.question_id == cachedQuestion.question_id } ?: cachedQuestion } _cachedShuffledQuestions.value = updatedList } } } }catch (e: Exception){ _quState.value = QueryState.Error(exception = e) } } // 单独抽出生成打乱列表的逻辑,用户切换筛选条件时可主动调用 fun refreshShuffledList(rawList: List<CertQuestions>? = null) { val sourceList = rawList ?: (_quState.value as? QueryState.Success)?.queries ?: return _cachedShuffledQuestions.value = when (currentFilter) { "pass" -> sourceList.filter { it.knowAnswer }.shuffled() "new" -> sourceList.filter { !it.knowAnswer }.shuffled() "random25" -> sourceList.shuffled().take(25) "random65" -> sourceList.shuffled().take(65) else -> sourceList.shuffled() } } // 切换筛选条件时调用该方法 fun updateFilter(newFilter: String) { currentFilter = newFilter refreshShuffledList() }
UI层直接收集cachedShuffledQuestions作为HorizontalPager的数据源即可,不需要再在UI层做打乱操作。
方案二:UI层缓存打乱后的列表
如果筛选逻辑不需要和ViewModel绑定,也可以在UI层用rememberSaveable缓存打乱后的列表,仅筛选条件变化时重新生成:
// 缓存当前筛选条件 val filter by rememberSaveable { mutableStateOf("all") } // 缓存打乱后的题目列表,filter变化时自动重置 var lstQuestions by rememberSaveable(filter) { mutableStateOf<List<CertQuestions>>(emptyList()) } when (val result = mainDataViewModel.quState.collectAsState().value) { is QueryState.Error -> { Log.d("Error", "Error Occured") } is QueryState.Success -> { if (lstQuestions.isEmpty()) { // 仅列表为空时生成打乱序列 lstQuestions = when (filter) { "pass" -> result.queries.filter { it.knowAnswer }.shuffled() "new" -> result.queries.filter { !it.knowAnswer }.shuffled() "random25" -> result.queries.shuffled().take(25) "random65" -> result.queries.shuffled().take(65) else -> result.queries.shuffled() } } else { // 数据库更新时仅更新对应条目字段,保留顺序 lstQuestions = lstQuestions.map { cachedQ -> result.queries.find { it.question_id == cachedQ.question_id } ?: cachedQ } } } }
关于更新方法的说明
你当前的updateKnowAnswer调用逻辑没有问题,只是数据库更新后触发了上游Flow的回调,导致UI层重新打乱列表才出现的问题,修改完上述缓存逻辑后即可正常运行。
内容的提问来源于stack exchange,提问作者srituts
相关产品推荐
相关产品推荐

