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

Kotlin MVVM中如何合并Room多实体Live/Flow数据生成目标POJO

解决方案:使用MediatorLiveData合并多数据源生成ScreenPOJO

步骤1:在ViewModel中实现数据合并逻辑

在你的ViewModel中添加MediatorLiveData来监听三个数据源的变化,实时合并生成符合要求的ScreenPOJO列表:

class YourViewModel(private val repository: YourRepository) : ViewModel() {
    // 原有的LiveData定义
    val allPersons: LiveData<List<Person>> = repository.allPersons.asLiveData()
    val allHolidays: LiveData<List<Holiday>> = repository.allHolidays.asLiveData()
    val allSickness: LiveData<List<Sickness>> = repository.allSickness.asLiveData()

    // 合并后的LiveData,对外暴露不可变版本
    private val _combinedScreenData = MediatorLiveData<List<ScreenPOJO>>()
    val combinedScreenData: LiveData<List<ScreenPOJO>> = _combinedScreenData

    init {
        // 关联三个数据源,任意一个数据变更都会触发合并逻辑
        _combinedScreenData.addSource(allPersons) { combineData() }
        _combinedScreenData.addSource(allHolidays) { combineData() }
        _combinedScreenData.addSource(allSickness) { combineData() }
    }

    /**
     * 核心合并逻辑:根据人员、假期、病假数据生成ScreenPOJO列表
     */
    private fun combineData() {
        // 安全获取各数据源数据,为空时用空列表兜底
        val persons = allPersons.value ?: emptyList()
        val holidays = allHolidays.value ?: emptyList()
        val sicknesses = allSickness.value ?: emptyList()

        // 将假期、病假数据转为以personId为键的Map,提升查找效率
        val holidayMap = holidays.associateBy { it.personId }
        val sicknessMap = sicknesses.associateBy { it.personId }

        // 遍历人员列表,计算每个人员的viewTypeId
        val screenList = persons.map { person ->
            val personHoliday = holidayMap[person.id]
            val personSickness = sicknessMap[person.id]

            // 无记录时默认状态为false
            val isOnHoliday = personHoliday?.onHoliday ?: false
            val isOnSickness = personSickness?.onSickness ?: false

            // 按照规则生成viewTypeId
            val viewTypeId = if (!isOnHoliday && !isOnSickness) 1 else 2

            ScreenPOJO(viewTypeId, person.name)
        }

        // 更新合并后的数据
        _combinedScreenData.value = screenList
    }

    // 原有的insertPerson方法...
}

步骤2:在Activity中观察合并后的数据

在Activity或Fragment中监听combinedScreenData,实时更新UI:

viewModel.combinedScreenData.observe(this) { screenPOJOList ->
    // 这里处理UI更新逻辑,例如刷新RecyclerView
    screenPOJOList.forEach { pojo ->
        Log.d("ScreenData", "姓名:${pojo.personName},类型ID:${pojo.viewTypeId}")
    }
}

特殊场景处理

如果一个人员可能存在多条假期/病假记录(如多次请假),请修改combineData中的状态判断逻辑:

// 检查该人员是否有任意一条假期记录为true
val isOnHoliday = holidays.any { it.personId == person.id && it.onHoliday }
// 检查该人员是否有任意一条病假记录为true
val isOnSickness = sicknesses.any { it.personId == person.id && it.onSickness }

可选:使用Kotlin Flow实现合并(更现代方案)

若Repository返回的是Flow而非LiveData,可使用Flow的combine操作符简化实现:

// ViewModel中定义Flow
val combinedScreenFlow: Flow<List<ScreenPOJO>> = combine(
    repository.allPersons,
    repository.allHolidays,
    repository.allSickness
) { persons, holidays, sicknesses ->
    val holidayMap = holidays.associateBy { it.personId }
    val sicknessMap = sicknesses.associateBy { it.personId }
    persons.map { person ->
        val isOnHoliday = holidayMap[person.id]?.onHoliday ?: false
        val isOnSickness = sicknessMap[person.id]?.onSickness ?: false
        val viewTypeId = if (!isOnHoliday && !isOnSickness) 1 else 2
        ScreenPOJO(viewTypeId, person.name)
    }
}.flowOn(Dispatchers.IO)

// Activity中观察Flow
lifecycleScope.launch {
    viewModel.combinedScreenFlow.collect { screenList ->
        // 更新UI
    }
}

内容的提问来源于stack exchange,提问作者DevPeter

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 08:20:33