【问题标题】:Changes in MutableLiveData is reflected in observers before explicitly setting valueMutableLiveData 的变化在显式设置值之前反映在观察者中
【发布时间】:2019-08-16 07:18:05
【问题描述】:

在 ViewModel 中,我有一个 MutableLiveData,其泛型类型是自定义对象的列表。该对象有一个 selected 字段,可以从 UI 更改。现在,我使用 RecyclerView 来填充 UI,并使用 DiffUtil.Callback 来调度正确的更改。

选中“查看”的复选框时,片段中的回调(包含recyclerview)获取通知。然后调用 ViewHolder 中的一个方法来更改 item 的selected 值,然后调用setValue

片段

private fun observeViewModel() {
    viewModel.data.observe(viewLifecycleOwner, Observer {
        adapter.updateItems(items)
        this.items = items
    })
}

override fun onSelectionChange(position: Int) {
    viewModel.reverseSelection(position)
}

视图模型

fun reverseSelection(index: Int) {
    data.value?.let {
        if (it.size > index) {
            it[index].selected = !it[index].selected
            data.value = it
        }
    }

}

RecyclerView 适配器

fun updateItems(items: List<T>) {
    val diffCallback = BaseDiffCallback(this.items, items)
    val diffResult = DiffUtil.calculateDiff(diffCallback, false)
    this.items = items
    diffResult.dispatchUpdatesTo(this)
}

问题在于 DiffUtil 没有检测到任何更改,因为它显示片段中的 items 字段和 ViewHolder 中的数据值指向内存中的同一对象。通过日志语句,我注意到在对 ViewHolder 中的数据显式调用 setValue 之前以及在触发观察者之前,片段中的项目已经反映了更改。

请问,在我在 ViewHolder 中调用 setValue 之前,我如何才能更改 selection 状态而不让 Fragment 中的项目反映该状态?


编辑

我非常感谢那些花时间为我的问题提供解决方案的好心用户。但是,由于我没有很好地解释我的问题,因此建议的解决方案对我不起作用。

我错过的一个关键细节是我正在使用数据绑定,并且模型的检查状态决定了ViewHolder 中其他视图的状态。另外,我之前打错了,reverseSelection 不在ViewHolder 中,而是ViewModel。最后,我将AdapterViewHolder 用于其他数据模型。

因此,selected 变量驻留在数据类中非常重要,否则,在查看 HashMap 或 Set 中的元素时会产生性能开销;检查position 处的项目是否被选中。数据集可能非常大。另外,因为我在其他数据模型中使用RecyclerView.AdapterViewHolder,所以我觉得使用HashMap或Set并不是最好的解决方案。

现在,我不再依赖 ViewHolder 来处理更改的数据模型的传播。因此,新的更新是:

视图模型

fun reverseSelection(index: Int): Boolean {
    return data.value?.let {
        if (it.size > index) {
            it[index].selected = !it[index].selected
            true
        } else false
    } ?: false

}

片段

override fun onSelectionChange(position: Int) {
        if (viewModel.reverseSelection(position)) {
            adapter.notifyItemChanged(position, 0)
        }
    }

我不知道这是否是最好的解决方案,但我会暂时使用它。

【问题讨论】:

    标签: android kotlin viewmodel android-livedata


    【解决方案1】:

    在适配器中,我将创建一个哈希映射来存储所选项目的索引,而不是直接修改项目。这将确保对象不会被修改,但您也会存储选定的索引。

    【讨论】:

    • 感谢您的建议。当我稍后使用选定的项目时,将它们存储在 ViewHolder 中以便片段可以轻松访问它不是更有意义吗?此外,我不会这样做,因为我需要选定的项目来更新 UI,除非我会在 HashMap 中搜索 onBindViewHolder 中的匹配索引;我认为这会很慢。请问,你怎么看?
    【解决方案2】:

    DiffUtil 是一个类,可以计算两个不同列表之间的差异,并输出更新操作列表,将第一个列表转换为第二个列表。 因此,如果您更改对同一列表执行数据更改,则不会调用 diffUtil 类。

    您在 MutableLivedata 中观察到的变化是因为您在选择项目时更改了模型列表。因此,每当您观察到的任何值得到更新时,mutablelivedate 都会调用 onChange 方法进行通知。

    要记录可选视图的变化,您可以在模型类中为其创建一个变量,然后可以访问整个对象。或者,您可以查看整个列表,因此无论何时修改列表,您都可以观察 onChange() 方法的变化。

    【讨论】:

    • 感谢您的帮助,但我不认为我说得对。我应该查看整个列表并且只更改模型类中的一个变量吗?这正是我正在做的。
    【解决方案3】:

    创建一个包含所选项目位置的集合。当用户从 recycerview 中选择一个项目时,如果该项目在集合中不存在,则将该项目添加到哈希集中,但如果存在,则从集合中删除该项目。然后通知适配器更新屏幕上的视图。现在将哈希集作为参数传递给单击侦听器中的 onSelectionChange() 侦听器方法。

    fun reverseSelection(index: Int) {
                data.value?.let {
                    if (set.contains(it)) {
                       //Uncheck the checkbox
                       set.remove(it)
                        return
                    }
                    set.add(it)
                       //Check the checkbox
    
                    //Call the interface/listener method here and pass the hashset as a parameter to it. The set consist of the seletected positions.
                    //Notify the adapter by calling notifyAdapterSetChanged()
                }
    
            }
    

    您的视图模型中不需要任何逻辑来反转所选项目。

    【讨论】:

    • 谢谢。拜托,我已经更新了我的问题以解释我认为该解决方案对我不起作用,然后添加了我正在使用的当前解决方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-09-18
    • 1970-01-01
    • 2022-12-14
    • 2020-08-09
    • 1970-01-01
    相关资源
    最近更新 更多