You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Jetpack Compose中Room查询实现问题:按角色与法术等级筛选

核心问题分析

  1. ViewModel事件处理逻辑失效

    • 你在onEvent中拿到GetSpellByCharacterAndLevel事件后,没有使用事件本身携带的characterId和spellLevel,反而读取state.value中的旧值;
    • 调用dao.getSpellByCharacterAndLevel()后仅获取了Flow,但未收集Flow的查询结果并更新到StateFlow,导致UI无法获取到实际的法术数据;
    • Event类的参数是私有属性,ViewModel无法访问,需改为val修饰。
  2. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.09 07:25:37