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

Android Kotlin三层嵌套RecyclerView数据关联错误求助

Android Kotlin三层嵌套RecyclerView数据关联错误解决

问题背景

项目采用三层嵌套RecyclerView:父层展示楼层,子层展示对应楼层的房间,最内层展示对应房间的设备。基于MVVM架构,通过Room数据库获取数据,但ViewModel的数据组装逻辑错误,导致数据展示错乱:

  • 期望结构:Floor1 -> RoomA -> [Device1, Device2] | RoomB -> [Device3] || Floor2 -> RoomC -> [Device4, Device5] | RoomD -> [Device6, Device7]
  • 实际问题:所有楼层包含全部房间,所有房间包含全部设备

错误原因

ViewModel的loadRooms()方法中,floorsList、roomsList、devicesList在循环外部初始化,属于全局可变列表。每次遍历楼层/房间时,新数据会追加到同一个列表中,而非为每个层级创建独立列表,最终导致数据累积错乱:

  • 遍历第二个房间时,devicesList会包含第一个房间的所有设备+当前房间设备
  • 遍历第二个楼层时,roomsList会包含第一个楼层的所有房间+当前楼层房间

修正步骤

1. 优化Room DAO查询(减少ViewModel冗余处理)

在DAO层直接添加DISTINCT关键字,返回去重后的数据,避免在ViewModel中重复处理:

@Dao
interface DevicesInformationDao {

    @Insert(onConflict = OnConflictStrategy.IGNORE)
    suspend fun insertAddedDevicesToDatabase(addedDevicesInformation: AddedDevicesInformation)

    // 返回去重后的所有楼层
    @Query("SELECT DISTINCT floor_name from added_device_information")
    fun readAllFloorsFromDatabase(): LiveData<List<String>>

    // 返回指定楼层下去重后的房间列表
    @Query("SELECT DISTINCT room_name from added_device_information where floor_name =:floor")
    suspend fun readAllRoomsOnAFloor(floor: String): List<String>
    
    // 返回指定房间下去重后的设备列表
    @Query("SELECT DISTINCT device_name from added_device_information where room_name =:room")
    suspend fun readAllDevicesInRoom(room: String): List<String>
}

2. 重构ViewModel数据组装逻辑

将列表初始化移到对应循环内部,确保每个层级拥有独立的子数据列表;同时改用Flow简化数据观察,优化协程调度:

class ControlPanelViewModel(private val repository: ControlPanelRepository) : ViewModel() {

    // 转换为排序后的楼层LiveData
    private val _allFloors = repository.getAllFloors.map { it.sorted() }
    val allFloors: LiveData<List<String>> = _allFloors

    private val _floorListLiveData = MutableLiveData<List<FloorsDataClass>>()
    val floorList: LiveData<List<FloorsDataClass>> get() = _floorListLiveData

    fun loadHierarchyData() {
        viewModelScope.launch {
            // 监听楼层数据变化,实时刷新层级结构
            allFloors.asFlow().collect { floors ->
                val floorHierarchy = mutableListOf<FloorsDataClass>()
                
                floors.forEach { floorName ->
                    val rooms = repository.getAllRooms(floorName)
                    val roomHierarchy = mutableListOf<RoomsDataClass>()
                    
                    rooms.forEach { roomName ->
                        val devices = repository.getAllDevices(roomName)
                        // 为当前房间创建独立的设备列表
                        val deviceItems = devices.map { DevicesDataClass(it) }
                        roomHierarchy.add(RoomsDataClass(roomName, deviceItems))
                    }
                    
                    // 为当前楼层创建独立的房间列表
                    floorHierarchy.add(FloorsDataClass(floorName, roomHierarchy))
                }
                
                _floorListLiveData.postValue(floorHierarchy)
            }
        }
    }
}

3. 调整数据类与适配器适配(可选,优化设备展示)

如果需要每个设备作为独立列表项,修改数据类:

// 修改为单个设备名,而非列表
data class DevicesDataClass(val machine: String)

对应调整DevicesAdapter的绑定逻辑:

override fun onBindViewHolder(holder: MachinesViewHolder, position: Int) {
    val currentMachine = devicesList[position]
    holder.binding.tvDevice.text = currentMachine.machine
}

4. 更新Fragment调用

替换原loadRooms()为修正后的方法:

override fun onCreateView(
    inflater: LayoutInflater,
    container: ViewGroup?,
    savedInstanceState: Bundle?
): View? {
    _binding = FragmentControlPanelBinding.inflate(layoutInflater)
    adapter = FloorsAdapter(requireActivity())
    controlPanelViewModel = ViewModelProvider(this, factory)[ControlPanelViewModel::class.java]
    controlPanelViewModel.loadHierarchyData()
    return binding.root
}

关键说明

  • 核心修正点:为每个层级创建独立的子列表,避免全局列表的累积添加
  • DAO层去重:减少ViewModel的冗余处理,提升查询效率
  • Flow转换:简化LiveData的观察逻辑,确保数据变化时实时刷新层级结构

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.23 03:06:38