再次自存强行观察LiveData是不是坏代码?

Is it bad code to forcibly observe LiveData by saving itself again?

我正在使用嵌套的 recyclerview。

图中red boxRoutine Item (Parent Item)blue box是Routine Item中的Detail Item (Child Item)

您可以通过单击 ADD ROUTINE button 动态添加 parent item

同理,点击parent itemADD button即可动态添加child items

因此,这个函数工作得很好。

但是问题出在我写的代码上。

我使用 ViewModel 来观察和更新父项 addition/deletion。

但是,它不会观察到父项中详细信息项的变化。

我认为是因为LiveData只检测对List的增删。

所以我放置了 _items.value = _items.value 代码以使其在添加和删除子项时可以观察到。

这样,我什至不必在 child adapter 中使用像 notifyDataSetChanged() 这样的更新代码。 最后还是成功了,不知道这个代码对不对。

如果您需要其他代码,请告诉我!

在Fragment.kt

class WriteRoutineFragment : Fragment() {
    private var _binding : FragmentWriteRoutineBinding? = null
    private val binding get() = _binding!!
    private lateinit var adapter : RoutineAdapter
    private val vm : WriteRoutineViewModel by viewModels { WriteRoutineViewModelFactory() }

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

        adapter = RoutineAdapter(::addDetail, ::deleteDetail)
        binding.rv.adapter = this.adapter
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        
        getTabPageResult()

        // RecyclerView Update
        vm.items.observe(viewLifecycleOwner) { updatedItems ->
            adapter.setItems(updatedItems)
        }
    }

    private fun getTabPageResult() {
        val navController = findNavController()
        navController.currentBackStackEntry?.also { stack ->
            stack.savedStateHandle.getLiveData<String>("workout")?.observe(
                viewLifecycleOwner, Observer { result ->
                    vm.addRoutine(result) // ADD ROUTINE
                    stack.savedStateHandle?.remove<String>("workout")
                }
            )
        }
    }

    private fun addDetail(pos: Int) {
        vm.addDetail(pos)
    }

    private fun deleteDetail(pos: Int) {
        vm.deleteDetail(pos)
    }
}

ViewModel

class WriteRoutineViewModel : ViewModel() {
    private var _items: MutableLiveData<ArrayList<RoutineModel>> = MutableLiveData(arrayListOf())

    val items: LiveData<ArrayList<RoutineModel>> = _items

    fun addRoutine(workout: String) {
        val item = RoutineModel(workout, "TEST")
        _items.value?.add(item)
//        _items.value = _items.value
    }

    fun addDetail(pos: Int) {
        val detail = RoutineDetailModel("TEST", "TEST")
        _items.value?.get(pos)?.addSubItem(detail) // Changing the parent item's details cannot be observed by LiveData.
        _items.value = _items.value // is this right way?
    }

    fun deleteDetail(pos: Int) {
        if(_items.value?.get(pos)?.getSubItemSize()!! > 1)
            _items.value?.get(pos)?.deleteSubItem() // is this right way?
        else
            _items.value?.removeAt(pos)
        _items.value = _items.value // is this right way?

    }
}

使用具有可变列表类型的 LiveData 时,这是非常标准的做法。代码看起来有点味道,但是很常见,我觉得是可以接受的,了解LiveData的人就会明白你的代码在做什么。

但是,如果将它们与 RecyclerView 一起使用,我更喜欢使用只读列表和不可变模型对象。它不太容易出错,如果你想使用 ListAdapter,它是必需的,它比常规 Adapter 的性能要好得多。每次有任何更改时,您当前的代码都会将整个列表重新加载到 RecyclerView 中,这会使您的 UI 感到迟钝。 ListAdapter 在后台线程上自动分析您的 List 的哪些项目具体更改,并且仅重新绑定更改的项目。但每次发生变化时都需要一个全新的 List 实例,因此如果你想支持使用它,只使用只读列表是有意义的。