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
相关产品推荐
相关产品推荐

