如何在 Android 中处理 3 个嵌套回收器视图中的数据 [Kotlin]

2024-03-25

我有一个由三个回收者视图组成的结构。因此,有一个父回收器视图包含一个子回收器视图,而该子回收器视图又包含一个子回收器视图。

为了理解,就像第一个回收者视图是楼层总数。第二个回收者视图是房间总数,第三个回收者视图是设备总数。

我有一个房间数据库,我可以在其中编写查询以接收总楼层、房间、设备作为字符串列表。因为它们作为字符串值存储在数据库中。

对于查询楼层来说,这是一个简单的查询。为了查询房间,我传递楼层位置,为了查询连接的设备,我传递房间值。

该项目遵循 MVVM 架构,因此存在 DAO,数据从 DAO 传送到存储库,存储库进一步传送到 Viewmodel,然后使用 LiveData 从 Fragment 进行观察。

主要问题:所以大部分获取楼层、房间和设备的逻辑都发生在 ViewModel 内部。我尝试了多种方法通过使用 livedata 或挂起函数从房间数据库中获取数据。但在这些循环中插入数据类中的值时会出现问题。我显然做错了什么(下面给出的代码),因为显示的数据格式不正确。我得到了楼层和房间的列表,但这就像所有房间都添加到每个楼层,而不是每个楼层的单独值。类似地,应连接到各个房间的所有设备都连接到所有房间。 如果有人能帮我解决这个问题,那将会非常有帮助,因为我在过去的两天里陷入了困境。谢谢!下面给出了所有代码,如果您需要更多理解,请在评论中告诉我。

实体文件

@Entity(tableName = "added_device_information")
data class AddedDevicesInformation(

    @ColumnInfo(name = "floor_name")
    var floorName: String? = "",

    @ColumnInfo(name = "room_name")
    var roomName: String? = "",

    @ColumnInfo(name = "machine_name")
    var machineName: String? = "",

    @ColumnInfo(name = "device_name")
    var deviceName: String? = "",

    @ColumnInfo(name = "factory_status")
    var factoryStatus: Boolean? = false,

    @PrimaryKey(autoGenerate = true)
    var id: Int? = null,
) {
}

DAO file

@Dao
interface DevicesInformationDao {

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

    //returns all floors in the database
    @Query("SELECT floor_name from added_device_information")
    fun readAllFloorsFromDatabase(): LiveData<List<String>>

    //returns all rooms associated with a floor
    @Query("SELECT room_name from added_device_information where floor_name =:floor")
    suspend fun readAllRoomsOnAFloor(floor: String): List<String>
    
    //Get all devices in a room
    @Query("SELECT device_name from added_device_information where room_name =:room")
    suspend fun readAllDevicesInRoom(room:String): List<String>
}

存储库文件

//This repository is for reading values from the database using [DevicesInformationDao]
class ControlPanelRepository(private val devicesInformationDao: DevicesInformationDao) {

    val getAllFloors: LiveData<List<String>> = devicesInformationDao.readAllFloorsFromDatabase()

    //for rooms associated with floors
    suspend fun getAllRooms(floor: String): List<String> = devicesInformationDao.readAllRoomsOnAFloor(floor)

    suspend fun getAllDevices(room: String): List<String> = devicesInformationDao.readAllDevicesInRoom(room)

}

ViewModel 文件(主要问题在这个文件中)

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

    //Variable for getting all floors
    private val _getAllFloors: LiveData<List<String>> = repository.getAllFloors
    private val getAllFloors: LiveData<List<String>> = _getAllFloors

    //setting FloorDataClass objects
    private val floorListLiveData = MutableLiveData<List<FloorsDataClass>>()
    val floorList: LiveData<List<FloorsDataClass>> get() = floorListLiveData

    fun loadRooms() {

        val floorsList = mutableListOf<FloorsDataClass>()
        val roomsList = mutableListOf<RoomsDataClass>()
        val devicesList = mutableListOf<DevicesDataClass>()

        var distinctFloors: List<String>
        var distinctRooms: List<String>
        var distinctDevices: List<String>

        getAllFloors.observeForever(Observer {

            viewModelScope.launch(Dispatchers.Main) {

                distinctFloors = it.distinct().sorted()

                for (floorName in distinctFloors) {
                    val rooms = repository.getAllRooms(floorName)

                    distinctRooms = rooms.distinct()

                    for (roomName in distinctRooms) {

                        distinctDevices = repository.getAllDevices(roomName)

                        devicesList.add(DevicesDataClass(distinctDevices))

                        roomsList.add(RoomsDataClass(roomName, devicesList))

                        floorsList.add(FloorsDataClass(floorName, roomsList))

                        floorListLiveData.postValue(floorsList)
                        Timber.d("$floorsList")

                    }
                }
            }
        })
    }
}

片段文件:

 override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View? {
        _binding = FragmentControlPanelBinding.inflate(layoutInflater)

        adapter = FloorsAdapter(activity)
controlPanelViewModel = ViewModelProvider(this, factory)[ControlPanelViewModel::class.java]
        controlPanelViewModel.loadRooms()
        return binding.root
    }


 override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        binding.rvFloors.adapter = adapter
        binding.rvFloors.layoutManager = LinearLayoutManager(requireContext())
        binding.rvFloors.setHasFixedSize(true)

        controlPanelViewModel.floorList.observe(viewLifecycleOwner, Observer {
            adapter.floorList(floors = it)
            Timber.d("List is $it")
        })
    }

数据类别 - 楼层数据类

data class FloorsDataClass(val floor: String, val rooms: List<RoomsDataClass>) {
}

房间数据类

data class RoomsDataClass(val room:String, val devices: List<DevicesDataClass>) {
}

设备数据类

data class DevicesDataClass(val machine: List<String>) {
}

回收站视图适配器

  • 地板适配器
open class FloorsAdapter(
    private val activity: FragmentActivity?
) : RecyclerView.Adapter<FloorsAdapter.FloorViewHolder>() {

    private var floorList = emptyList<FloorsDataClass>()

    inner class FloorViewHolder(
        val binding: ListItemControlPanelFloorsBinding,
        val roomsAdapter: RoomsAdapter
    ) : RecyclerView.ViewHolder(binding.root) {}

    override fun onCreateViewHolder(
        parent: ViewGroup,
        viewType: Int
    ): FloorViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ListItemControlPanelFloorsBinding.inflate(layoutInflater, parent, false)

        //Create adapter per floor
        val adapter = RoomsAdapter(activity)
        binding.rvRoomControlPanel.adapter = adapter
        binding.rvRoomControlPanel.layoutManager = LinearLayoutManager(activity)
        return FloorViewHolder(binding, adapter)
    }

    @SuppressLint("NotifyDataSetChanged", "SetTextI18n")
    override fun onBindViewHolder(holder: FloorsAdapter.FloorViewHolder, position: Int) {
        //For a given floor set the rooms on that floor's room adapter
        val currentFloor = floorList[position]
        holder.apply {
            roomsAdapter.roomList(currentFloor.rooms)
            binding.tvFloor.text = "Floor : ${currentFloor.floor}"
            Timber.d("Rooms on floor: $currentFloor are : ${currentFloor.rooms}")
        }
    }

    @SuppressLint("NotifyDataSetChanged")
    fun floorList(floors: List<FloorsDataClass>) {
        this.floorList = floors
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int {
        return floorList.size
    }
}
  • 房间适配器
open class RoomsAdapter(
    private val activity: FragmentActivity?
) : RecyclerView.Adapter<RoomsAdapter.RoomViewHolder>() {

    private var roomList = emptyList<RoomsDataClass>()

    inner class RoomViewHolder(
        val binding: ListItemControlPanelRoomsBinding,
        val machinesAdapter: DevicesAdapter
    ) : RecyclerView.ViewHolder(binding.root) {}

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RoomViewHolder {
        val layoutInflater = LayoutInflater.from(parent.context)
        val binding = ListItemControlPanelRoomsBinding.inflate(layoutInflater, parent, false)

        //setting machines for each room
        val adapter = DevicesAdapter()
        binding.rvMachineControlPanel.adapter = adapter
        binding.rvMachineControlPanel.layoutManager = LinearLayoutManager(activity)
        return RoomViewHolder(binding, adapter)
    }

    @SuppressLint("SetTextI18n")
    override fun onBindViewHolder(holder: RoomViewHolder, position: Int) {
        val currentRoom = roomList[position]
        Timber.d("Current room : $currentRoom")
        holder.apply {
            binding.tvRoom.text = "Room : ${currentRoom.room}"
            machinesAdapter.devicesLists(currentRoom.devices)
        }
    }

    @SuppressLint("NotifyDataSetChanged")
    fun roomList(room: List<RoomsDataClass>) {
        this.roomList = room
        Timber.d("List received : $room")
        notifyDataSetChanged()
    }

    override fun getItemCount(): Int {
        return roomList.size
    }
}
  • 设备适配器
class DevicesAdapter : RecyclerView.Adapter<DevicesAdapter.MachinesViewHolder>() {

    private var devicesList = emptyList<DevicesDataClass>()

    inner class MachinesViewHolder(val binding: ListItemControlPanelMachinesBinding) :
        RecyclerView.ViewHolder(binding.root)

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MachinesViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val binding = ListItemControlPanelMachinesBinding.inflate(inflater, parent, false)
        return MachinesViewHolder(binding)
    }

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

    override fun getItemCount(): Int {
        return devicesList.size
    }

    fun devicesLists(machine: List<DevicesDataClass>) {
        this.devicesList = machine
    }
}

Edit 1:我需要片段数据为: 楼层 1 -> 房间 A -> 设备 1、设备 2 | RoomB -> 设备 3 || 2 楼 -> RoomC -> 设备、设备 5 | D室->设备,设备7

我得到的数据为: Floor1 -> RoomA -> Device1、Device2、Device3、Device 4 ....(直到最后一个设备)| RoomBA -> 设备 1、设备 2、设备 3、设备 4 ....(直到最后一个设备)|| 2楼 -> 相同


代码中存在一些问题loadRooms

  • 如果房间或设备每个楼层/房间都是唯一的,则不应有外部列表。你正在做onedeviceList 并不断向其中添加内容,而不是为每个房间创建单独的 deviceList。类似地,您正在创建一个 roomList 并添加到其中,而不是每层单独的列表。
  • 您不应将楼层列表发布在最内层循环内

它应该看起来像这样:

fun loadRooms() {

    getAllFloors.observeForever(Observer {

        viewModelScope.launch(Dispatchers.Main) {

            val distinctFloorNames = it.distinct().sorted()
            val floorsList = mutableListOf<FloorsDataClass>()

            // Loop over floors
            for (floorName in distinctFloorNames) {

                // At *each* floor prepare a list of rooms
                val roomNames = repository.getAllRooms(floorName)
                val distinctRoomNames = roomNames.distinct().sorted()
                val roomsList = mutableListOf<RoomDataClass>

                // Loop over rooms in the floor
                for (roomName in distinctRoomNames) {

                    // In *each* room prepare a list of devices
                    val deviceNames = repository.getAllDevices(roomName)
                    val distinctDeviceNames = deviceNames.distinct().sorted()

                    // Transform the list of strings to a list of
                    // DeviceDataClass objects
                    val deviceData = distinctDeviceNames.map { dev -> DeviceDataClass(dev) }

                    // Add the device list to a room object
                    // and add the room to the room list
                    roomsList.add(RoomsDataClass(roomName, deviceData))
                }
                
                // Add the room list to a floor object and add
                // the floor to the floor list
                floorsList.add(FloorsDataClass(floorName, roomsList))
            }
            
            // Post the completed floor list
            floorListLiveData.postValue(floorsList)
            Timber.d("$floorsList")
        }
    })
}

修改为使得DevicesDataClass是单个字符串,而不是字符串列表(因为房间已经包含设备列表)

data class DeviceDataClass(val machine: String) {}
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

如何在 Android 中处理 3 个嵌套回收器视图中的数据 [Kotlin] 的相关文章

  • 您的手机中未安装应用程序

    我在模拟器中运行该应用程序 它成功运行 并且应用程序的图标显示在模拟器菜单中 但是当我尝试从模拟器菜单再次运行该应用程序时 它不允许我从中运行并显示 Toast 您的手机中未安装应用程序 在图像中 红色圆形是我的应用程序图标 如果您有您的M
  • Mediaplayer 播放几次后停止播放

    我有一个按钮 按下它会播放一个随机声音剪辑 然后播放另一个声音剪辑 然后通过一个媒体播放器播放另一个声音剪辑 但是多次按下该按钮 15 20 次 后 所有音频都会停止 我在播放最后一个音频剪辑后释放媒体播放器 所以我不认为这是原因 有什么指
  • onScale 和 Canvas - 缩放图像后如何调整原点?

    我有一个非常简单的测试应用程序 带有自定义组件MyView java https github com afarber android newbie blob master TestScroll src de afarber testscr
  • 如何在android中以编程方式自动执行触摸

    我有一个RelativeLayout我想在不触摸屏幕的情况下执行触摸事件想要给出Toast如果它确实被触摸或没有被触摸 请抛出消息 我已经尝试过下面的方法 但它似乎不起作用 MotionEvent event MotionEvent obt
  • 如何在WPF中使用一次性视图模型?

    如果视图模型引用非托管资源或具有事件处理程序 例如调度程序计时器上的处理已过去 如何确保视图模型得到正确处理 在第一种情况下 终结器是一种选择 虽然并不理想 但在后者中 它永远不会被调用 我们如何判断何时不再有视图附加到视图模型 我通过执行
  • 带有 ListTiles 和按钮行的 Flutter 下拉菜单

    我正在尝试构建一个自定义下拉菜单 如下所示 我已经成功地实现了ListTiles and Row of Buttons没有下拉菜单 但我不确定如何将所有内容嵌套在下拉菜单类中 这是我到目前为止所得到的 class HomePage exte
  • 片段开始时显示用于编辑文本的键盘

    当我的片段开始时 我希望我的编辑文本成为焦点 让用户开始输入内容 我可以使用 requestFocus 将其聚焦 但无法显示键盘 我已经尝试过这两种方法 edit EditText view findViewById R id search
  • 删除对象时删除嵌套字段中的索引

    我仍在使用 Firebase 这次我有一个与删除对象相关的问题 我有如下结构 users UsErId1 name Jack email email protected cdn cgi l email protection UsErId2
  • 当 Android 上的脸部靠近屏幕时,以编程方式关闭屏幕

    我的应用程序是一个拨号器 当用户将手机靠近头部时 我需要关闭屏幕并防止单击控件 就像本机 Android 拨号器行为一样 我需要什么 API 级别以及如何以正确的方式做到这一点 我通过反汇编一个非常著名的 VoIP 应用程序找到了解决方案
  • 如何模糊视图

    I have a view having different colors I need to blur the background of that view for example There is LinearLayout in wh
  • Android 生命周期哪个事件在生命周期中只触发一次?

    我读过一些博客并访问了一些网站 我想知道哪个事件在生命周期中只触发了一次 阅读博客后我意识到onCreate 方法在生命周期内仅触发一次 我不知道我是对还是错 现在我的问题是 我想触发任何仅在我更改横向或纵向方向时触发一次的事件 而不是在启
  • WPF MVVM将DataTable绑定到DataGrid不显示数据

    我有一个简单的控件 其中包含一个 DataGrid 其中 ItemsSource 绑定到 DataTable 当我填充 DataTable 时 我可以看到 DataGrid 中添加了行 但没有显示任何数据 我没有为此 DataGrid 使用
  • Gradle创建多项目Jar

    因此 从 Gradle 和 Android Studio 诞生之初起 我就一直在使用它们 然而 我发现自己用头撞墙的次数有时远远超过了它的价值 我花了一天半的时间试图解决我目前的困境 在我工作的地方 我们使用很多共享库项目 这意味着与 Gr
  • Android wifi的信号强度[重复]

    这个问题在这里已经有答案了 可能的重复 Android 如何监控WiFi信号强度 https stackoverflow com questions 1206891 android how to monitor wifi signal st
  • 短信管理器在少于 160 个字符时发送多部分消息

    我编写了一个使用短信管理器的应用程序 我用的方法sendTextMessage 但这行不通 现在我正在使用sendMutlipartTextMessage 这是工作 但当它大约 60 个字符时 它会发送多部分消息 这个是正常的 我读过的所有
  • 是否可以用 C# 为 Android 编写应用程序?

    我们都知道Android运行Dalvik VM程序 通常开发人员用 Java 编写程序并将其编译为 Dalvik 字节码 我想知道是否有可能创建一个可以接受 C 代码并将其编译为 Dalvik 字节码的编译器 嗯 这是一种选择 或者您可以在
  • Android onclicklistener 在第一次点击时不起作用

    我有一个带有默认文本的 EditText 现在 当用户单击该 EditText 时 默认文本应该更改为某些内容 我所拥有的是 我单击 EditText 光标出现在默认文本之后 没有任何反应 当我再次单击时 onClickListener 就
  • 图标和导航视图之间的左边距

    我必须在图标和图标之间添加左边距NavigationView 如下图中箭头所示 我知道根据谷歌规范 这个边距必须有16dp但我需要改变它 我努力了
  • 运行 Espresso 测试时在 Android studio 中找不到属性 android:forceQueryable

    我已经使用 android studio 录制了我的 Android 应用程序 Espresso 测试记录浓缩咖啡测试选项中Run菜单 在记录的最后 我用自己的文件名保存了测试 单击保存按钮后 IDE 会自动在以下位置创建文件Android
  • Android - 按下后退按钮时停止 AsyncTask 并返回到上一个 Activity

    我有一个 AsyncTask 我希望它在按下后退按钮时停止执行 我还希望应用程序返回到之前显示的 Activity 看来我已经成功停止了任务 但应用程序没有返回到之前的活动 有任何想法吗 这是我的代码的摘录 private class My

随机推荐