如何在ViewModel中利用Repository的Kotlin Flow生成LightGroupStatus流
问题描述
我们从Repository获取到一个Flow<List<ViewLights>>,数据类定义如下:
data class ViewLights( val groupId: Long?, val group: String?, val groupType: String?, val light: String, val isTurnedOn: Boolean, )
该流包含带分组信息的灯光列表(示例:1 | Living Room | Room | Wall Light | true)。需要在ViewModel中基于此流生成并更新LightGroupStatus流,数据类定义如下:
data class LightGroupStatus( val groupId: Long, val groupName: String, val groupLightsCount: Int, val groupLightsCountOn: Int, )
要求新流需过滤groupType为"Room"的分组,并统计每组的总灯数(groupLightsCount)和亮灯数(groupLightsCountOn)。当前ViewModel代码如下:
@HiltViewModel class LightsViewModel @Inject constructor( private val repository: LightRepository, private val lightUseCases: LightUseCases, ) : ViewModel() { private val _lightsAndGroups: Flow<List<ViewLights>> = lightUseCases.getAllLights() val lightsAndGroups = _lightsAndGroups private val _lightGroupStatus = MutableStateFlow<List<LightGroupStatus?>> (emptyList()) val lightGroupStatus = _lightGroupStatus.asStateFlow()
如何将_lightsAndGroups中的分组信息填充到_lightGroupStatus中?
解决方案
可以通过流的转换与收集来实现需求,以下提供两种可行方案:
方案一:直接转换生成目标流(推荐)
无需维护MutableStateFlow,直接利用map操作符对原流做转换,代码更简洁:
@HiltViewModel class LightsViewModel @Inject constructor( private val repository: LightRepository, private val lightUseCases: LightUseCases, ) : ViewModel() { private val _lightsAndGroups: Flow<List<ViewLights>> = lightUseCases.getAllLights() val lightsAndGroups = _lightsAndGroups // 直接转换生成符合要求的LightGroupStatus流 val lightGroupStatus: Flow<List<LightGroupStatus>> = _lightsAndGroups .map { lightsList -> // 过滤出有效数据:groupType为Room,且groupId、group不为空 lightsList.filter { light -> light.groupType == "Room" && light.groupId != null && light.group != null } // 按groupId分组 .groupBy { it.groupId!! } // 转换为LightGroupStatus对象 .mapValues { (groupId, lightsInGroup) -> LightGroupStatus( groupId = groupId, groupName = lightsInGroup.first().group!!, groupLightsCount = lightsInGroup.size, groupLightsCountOn = lightsInGroup.count { it.isTurnedOn } ) } .values.toList() } }
方案二:收集到MutableStateFlow中
如果需要在ViewModel内部对lightGroupStatus做额外修改,可以通过launchIn配合collect更新状态:
@HiltViewModel class LightsViewModel @Inject constructor( private val repository: LightRepository, private val lightUseCases: LightUseCases, ) : ViewModel() { private val _lightsAndGroups: Flow<List<ViewLights>> = lightUseCases.getAllLights() val lightsAndGroups = _lightsAndGroups private val _lightGroupStatus = MutableStateFlow<List<LightGroupStatus>>(emptyList()) val lightGroupStatus = _lightGroupStatus.asStateFlow() init { // 监听原流变化,更新分组状态 _lightsAndGroups .map { lightsList -> lightsList.filter { light -> light.groupType == "Room" && light.groupId != null && light.group != null } .groupBy { it.groupId!! } .mapValues { (groupId, lightsInGroup) -> LightGroupStatus( groupId = groupId, groupName = lightsInGroup.first().group!!, groupLightsCount = lightsInGroup.size, groupLightsCountOn = lightsInGroup.count { it.isTurnedOn } ) } .values.toList() } .launchIn(viewModelScope) .collect { statusList -> _lightGroupStatus.value = statusList } } }
关键细节说明
- 过滤逻辑:先筛选出
groupType为"Room"且groupId、group不为空的数据,避免后续空指针异常 - 分组统计:用
groupBy按groupId聚合同组灯光,再通过size统计总灯数,count统计亮灯数 - 安全解包:因为已经过滤掉空值,所以可以安全使用
!!解包,也可以用requireNotNull替代以提升严谨性
内容的提问来源于stack exchange,提问作者Ohlsen
相关产品推荐
相关产品推荐

