RecyclerView 未使用 SubmitList() 和 LiveData 自动刷新

nei*_*e-i 4 android kotlin android-recyclerview android-livedata android-diffutils

情况如下:

我有一个RecyclerView正在Fragment使用DataBinding. 它的适配器是一个ListAdapter.

class MyFragment : Fragment() {
    override fun onCreateView(...) {
        val binding = // inflate layout with DataBindingUtil

        val myListAdapter = MyListAdapter()
        binding.recyclerView.adapter = myListAdapter

        val myViewModel: MyViewModel by activityViewModels()
        myViewModel.myLiveData.observe(viewLifecycleOwner, Observer {
            println("before submit: ${myListAdapter.currentList}")
            myListAdapter.submitList(it)
            println("after submit: ${myListAdapter.currentList}")
        })

        return binding.root
    }
}
Run Code Online (Sandbox Code Playgroud)

要显示的数据由 表示List<Double>。设置后,列表将用于初始化 a,MutableLiveData如下所示:

class MyViewModel : ViewModel() {
    val data = listOf<Double>(/* init with some values */)
    val myLiveData = MutableLiveData(data)

    fun onButtonClicked() {
        update()
        println(myLiveData.value) // LiveData is correctly updated according to data
    }
}
Run Code Online (Sandbox Code Playgroud)

注意:该update()方法会更改列表的值,例如:data[0] = someValue。它不使用clear()也不addAll()更新它。

  • 另外,ListAdapter.onCurrentListChanged()只为了在println()里面放而覆盖。

在这一点上,没有什么奇怪的。创建片段后,将RecyclerView显示初始列表。在 Logcat 中:

I/System.out: before submit: []
I/System.out: onCurrentListChanged: [] -> [4.07, 6.29, 13.85, 17.92, 21.42, 23.47, 1.85]
I/System.out: after submit: [4.07, 6.29, 13.85, 17.92, 21.42, 23.47, 1.85]
Run Code Online (Sandbox Code Playgroud)

目前,当我单击该按钮时,UI 不会刷新,因为我没有分配新列表,而仅更新当前列表。此外,如果我导航到另一个片段然后返回,则会RecyclerView 显示更新的值。

所以我在末尾添加以下内容onButtonClicked()

myLiveData.value = myLiveData.value
Run Code Online (Sandbox Code Playgroud)

但这不起作用,点击按钮后 UI 仍然没有自动更新。此外,Logcat 仅打印“提交前”和“提交后”,具有相同的ListAdapter.currentList值:

I/System.out: before submit: [4.88, 6.77, 13.85, 17.76, 20.93, 22.69, 1.85]
I/System.out: after submit: [4.88, 6.77, 13.85, 17.76, 20.93, 22.69, 1.85]
Run Code Online (Sandbox Code Playgroud)

对我来说,打印应该看起来像这样:

I/System.out: before submit: [4.07, 6.29, 13.85, 17.92, 21.42, 23.47, 1.85]
I/System.out: onCurrentListChanged: [4.07, 6.29, 13.85, 17.92, 21.42, 23.47, 1.85] -> [4.88, 6.77, 13.85, 17.76, 20.93, 22.69, 1.85]
I/System.out: after submit: [4.88, 6.77, 13.85, 17.76, 20.93, 22.69, 1.85]
Run Code Online (Sandbox Code Playgroud)

看起来ListAdapter.currentList甚至在调用之前就已经更新了值submitList()。我想这就是为什么RecyclerView不刷新的原因,因为提交的列表与当前的列表相同。
我最后还尝试了以下方法onButtonClicked()但没有成功:

//myLiveData.value = myLiveData.value
myLiveData.value = emptyList()
myLiveData.value = data
Run Code Online (Sandbox Code Playgroud)

和这个...

//myLiveData.value = myLiveData.value
myLiveData.value = emptyList()
//myLiveData.value = data
Run Code Online (Sandbox Code Playgroud)

...给了我:

I/System.out: before submit: [4.88, 6.77, 13.85, 17.76, 20.93, 22.69, 1.85]
I/System.out: after submit: [4.88, 6.77, 13.85, 17.76, 20.93, 22.69, 1.85]
I/System.out: onCurrentListChanged: [4.88, 6.77, 13.85, 17.76, 20.93, 22.69, 1.85] -> []
Run Code Online (Sandbox Code Playgroud)

现在onCurrentListChanged()被调用,但在 2 个打印的末尾而不是在中间。
此外,ListAdapter.currentList似乎在“提交后”打印中包含值,但实际上打印中为空onCurrentListChanged()。令人困惑...

我发现刷新的唯一方法RecyclerView是调用notifyDataSetChanged(). ListAdapter但这样的话,使用它的方法并没有什么好处submitList()

作为一个新手,我可能做错了什么,但我不知道是什么。
问题只是 UI 不刷新。我可以在 Locgcat 中看到数据正在正确更新。

提前致谢

编辑

class MyListAdapter() : ListAdapter<Double, MyViewHolder>(MyDiffCallBack()) {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        MyViewHolder.from(parent, itemCount)

    override fun onBindViewHolder(holder: MyViewHolder, position: Int) =
        holder.bind(getItem(position))

    override fun onCurrentListChanged(previousList: MutableList<Double>, currentList: MutableList<Double>) {
        super.onCurrentListChanged(previousList, currentList)
        println("onCurrentListChanged: $previousList -> $currentList")
    }
}

class MyViewHolder private constructor(private val binding: MyItemViewBinding) : RecyclerView.ViewHolder(binding.root) {

    fun bind(data: Double) {
        binding.dataTxt.text = data.toString()
    }

    companion object {
        fun from(parent: ViewGroup, itemCount: Int): MyViewHolder {
            val binding = // inflate layout
            binding.root.layoutParams.height = parent.height / itemCount // set the height proportionally
            return MyViewHolder(binding)
        }
    }
}

class MyDiffCallBack : DiffUtil.ItemCallback<Double>() {
    override fun areItemsTheSame(oldItem: Double, newItem: Double): Boolean = oldItem == newItem

    override fun areContentsTheSame(oldItem: Double, newItem: Double): Boolean = oldItem == newItem

}
Run Code Online (Sandbox Code Playgroud)

nei*_*e-i 5

这是一个与浅拷贝和深拷贝的区别有关的问题。
正如这篇文章所述ListAdapter

值得高兴的是,它保留了对 中提供的列表的ListAdapter 引用submitList()。始终确保提供包含其他项目实例的其他列表。如果对项的引用相同,DiffUtil则比较相同的项,始终相等,并且RecyclerView 不会更新