RecyclerView не обновляется автоматически с помощью submitList () и LiveData - PullRequest
2 голосов
/ 28 мая 2020

вот такая ситуация:

У меня 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
    }
}

Данные для отображения представлены List<Double>. После установки список используется для инициализации 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
    }
}

Примечание: метод 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]

На данный момент, когда я нажимаю кнопку, пользовательский интерфейс не обновляется sh, потому что я не назначал новый список, а только обновлял текущий. Кроме того, если я перейду к другому фрагменту, а затем вернусь, RecyclerView отображает обновленные значения.

Поэтому я добавляю следующее в конце onButtonClicked():

myLiveData.value = myLiveData.value

Но это не работает, после нажатия кнопки пользовательский интерфейс по-прежнему не обновляется автоматически. Более того, 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]

Для меня отпечатки должны выглядеть так:

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]

Похоже, что ListAdapter.currentList уже имеет обновленное значение еще до вызова submitList(). Полагаю, именно поэтому RecyclerView не обновляется, поскольку представленный список совпадает с текущим.
Я также безуспешно пробовал следующее в конце onButtonClicked():

//myLiveData.value = myLiveData.value
myLiveData.value = emptyList()
myLiveData.value = data

И это ...

//myLiveData.value = myLiveData.value
myLiveData.value = emptyList()
//myLiveData.value = data

... дает мне следующее:

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] -> []

Теперь вызывается onCurrentListChanged(), но в конце двух отпечатков, а не в в середине.
Кроме того, ListAdapter.currentList кажется, содержит значения в распечатках «после отправки», но на самом деле пусто в печати onCurrentListChanged(). Сбивает с толку ...

Я нашел единственный способ сделать RecyclerView refre sh - это позвонить notifyDataSetChanged(). Но тогда нет никакой пользы от использования ListAdapter и его метода submitList().

Как новичок ie, я, возможно, сделал что-то неправильно, но я не знаю что.
проблема только в том, что пользовательский интерфейс не обновляется. В Locgcat я вижу, что данные обновляются правильно.

Заранее спасибо

EDIT

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

}
...