Jetpack Compose中Room查询实现问题:按角色与法术等级筛选
核心问题分析
ViewModel事件处理逻辑失效
- 你在
onEvent中拿到GetSpellByCharacterAndLevel事件后,没有使用事件本身携带的characterId和spellLevel,反而读取state.value中的旧值; - 调用
dao.getSpellByCharacterAndLevel()后仅获取了Flow,但未收集Flow的查询结果并更新到StateFlow,导致UI无法获取到实际的法术数据; - Event类的参数是私有属性,ViewModel无法访问,需改为
val修饰。
- 你在
UI层对Event的作用完全误解
你在LazyColumn中直接把CustomListEvent对象当作法术列表数据展示,这是错误的——Event是用来通知ViewModel执行查询操作的指令,并非查询返回的结果数据。
修正方案
1. 修复Event与ViewModel逻辑
首先调整Event的参数可见性,再让ViewModel收集Room返回的Flow并更新StateFlow:
// 修正Event定义,参数改为val以便访问 sealed interface CustomListEvent { class GetSpellByCharacterAndLevel(val characterId: Int, val spellLevel: Int) : CustomListEvent } // 修正ViewModel逻辑 class CustomListViewModel(private val dao: CustomListDao) : ViewModel() { private val _state = MutableStateFlow(CustomListState()) val state = _state.asStateFlow() fun onEvent(event: CustomListEvent) { when (event) { is CustomListEvent.GetSpellByCharacterAndLevel -> { // 使用事件携带的参数发起查询 dao.getSpellByCharacterAndLevel(event.characterId, event.spellLevel) .onEach { spells -> // 将查询结果更新到StateFlow _state.update { it.copy(currentSpells = spells) } } .launchIn(viewModelScope) // 用viewModelScope管理Flow生命周期,避免内存泄漏 } } } } // 扩展CustomListState,添加存储当前法术列表的字段 data class CustomListState( val characterFk: Int = 0, val spellLevel: Int = 0, val currentSpells: List<CustomList> = emptyList() )
2. 修复UI层展示逻辑
先触发Event让ViewModel执行查询,再收集StateFlow的数据来展示法术列表:
@Composable fun SpellListScreen(viewModel: CustomListViewModel) { val state = viewModel.state.collectAsState() val characterId = viewModel.characterFkTemp.value // 初始化时触发0级法术查询,切换等级时可再次调用onEvent LaunchedEffect(Unit) { viewModel.onEvent(CustomListEvent.GetSpellByCharacterAndLevel(characterId, 0)) } LazyColumn(modifier = Modifier) { items(state.value.currentSpells) { spell -> Text(text = spell.spellTitle) // 展示法术标题,而非Event的toString内容 } } }
3. 多等级法术展示优化(可选)
如果需要同时展示不同等级的法术列表,可以让ViewModel直接返回对应等级的Flow,无需通过Event:
// ViewModel新增方法 fun getSpellsByLevel(characterId: Int, spellLevel: Int): Flow<List<CustomList>> { return dao.getSpellByCharacterAndLevel(characterId, spellLevel) } // UI层针对每个等级单独收集数据 @Composable fun MultiLevelSpellScreen(viewModel: CustomListViewModel) { val characterId = viewModel.characterFkTemp.value val level0Spells = viewModel.getSpellsByLevel(characterId, 0).collectAsState(initial = emptyList()) val level1Spells = viewModel.getSpellsByLevel(characterId, 1).collectAsState(initial = emptyList()) LazyColumn { item { Text("0级法术") } items(level0Spells.value) { Text(it.spellTitle) } item { Text("1级法术") } items(level1Spells.value) { Text(it.spellTitle) } } }
内容的提问来源于stack exchange,提问作者IanOro
相关产品推荐
相关产品推荐

