【问题标题】:Is it bad code to forcibly observe LiveData by saving itself again?通过再次保存自己来强制观察 LiveData 是不是坏代码?
【发布时间】:2021-08-31 18:03:06
【问题描述】:

我正在使用嵌套的 recyclerview。

图中red boxRoutine Item (Parent Item)blue box是日常项目中的Detail Item (Child Item)

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

同样,child items可以通过点击parent itemADD button动态添加。

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

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

我使用ViewModel 来观察和更新父项添加/删除。

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

我认为这是因为LiveData 只检测到列表的添加和删除。

所以我放了_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?

    }
}

【问题讨论】:

    标签: android kotlin android-recyclerview android-viewmodel


    【解决方案1】:

    当使用具有可变列表类型的 LiveData 时,这是非常标准的做法。代码看起来像一种气味,但它很常见,我认为它是可以接受的,了解 LiveData 的人会理解您的代码在做什么。

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

    【讨论】:

    • 感谢您的回复。所以,你的回答是我的代码是一个通用的方法,但是当使用RecyclerView 像我的应用程序有很多变化时,更建议使用一个名为ListAdapter 的类?而且我对ListAdapter不熟悉,所以搜索了一下,发现内部使用DiffUtil有更简单的方法吗?
    • 不确定您所说的更简单的方法是什么意思。大概您的模型类有一些东西可以唯一地标识列表中的项目,例如 ID 或唯一名称。比较 areItemsTheSame。如果您为模型类使用数据类,则可以对areContentsTheSame 使用简单的相等性检查。所以这很容易开始。
    • 我认为 value = value 可以接受但并不理想,无论你是否使用 ListAdapter。我能想到的不对任何 RecyclerView 使用 ListAdapter 的唯一原因是为自己节省五分钟,而不必编写 DiffUtil 回调,但会牺牲运行时性能并在插入或删除项目时拥有漂亮的动画。
    • 即使您不使用 ListAdapter,我认为最好不要公开公开任何类的可变集合。公开暴露的可变集合会产生歧义,这可能不利于封装。我自己只是一个单独的开发人员,但是当我回到几个月没有看过的代码时,将我的代码设计成让其他团队成员可以一起工作对我有很大帮助。
    • 想象你在一个团队中工作,其他人正在编写适用于你的 ViewModel 的 Fragment。如果它暴露了一个 MutableList,那么他们不得不怀疑:我可以修改这个列表,还是会有不想要的副作用?它是可变的,因为我需要以某种 ViewModel 所期望的方式对其进行变异吗? 如果他们确实想使用 ListAdapter,他们必须在每次将其传递给适配器之前将列表复制到另一个列表中为了安全起见,在观察者中接收一个。
    猜你喜欢
    • 1970-01-01
    • 2021-01-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多