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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 03:07:08