Solo para su información, no estoy buscando exactamente una "solución", sino una explicación y una discusión que pueda ayudar a comprender un poco más cómo funcionan cosas aparentemente tontas como estas.
Estaba trabajando en este proyecto más grande cuando me di cuenta de que, en algún lugar, cierta lista no se actualizaba correctamente. Mirando un poco más de cerca, los elementos se estaban modificando correctamente, y si "se desplazaba hacia afuera" y hacia atrás, la información del elemento se mostraría correctamente.
Me topé con este artículo: ListAdapter no actualiza el elemento en RecyclerView
Pero la diferencia aquí es que, de hecho, se estaba llamando a DiffUtils, ¡pero de alguna manera newItem y oldItem eran iguales! Entiendo que la biblioteca asume que está usando Room o cualquier otro ORM que ofrece una nueva lista asíncrona cada vez que se actualiza , pero aquí está la cuestión. Si envío la lista "ingenuamente", DiffUtils ni siquiera se llama. Pero, si envío la lista como list.toMutableList() como sugieren algunos entonces, se llama a DiffUtils, pero de alguna manera los elementos, nuevos y antiguos, ya son los mismos, por lo tanto, nada se actualiza en ese momento (verificado esto colocando puntos de interrupción dentro areContentsTheSame ).
Te dejo aquí los fragmentos relevantes y un enlace a un proyecto de prueba que creé solo para poder encapsular el comportamiento y probarlo por separado de todo lo demás.
El Fragmento - simplemente llamando a la lista de envío
viewModel.items.observe(viewLifecycleOwner) { adapter.submitList(it.toMutableList()) }Ver modelo
private val _items = MutableLiveData<List<SimpleItem>>() val items: LiveData<List<SimpleItem>> get() = _items init { _items.value = ItemsRepo.getItems() } fun onItemClick(itemId: Int) { ItemsRepo.addItemCount(itemId) _items.value = ItemsRepo.getItems() }El "Repo" creo algunos objetos de datos ItemsRepo {
private var items = mutableListOf( SimpleItem(1), SimpleItem(2), SimpleItem(3), SimpleItem(4), SimpleItem(5) ) fun getItems(): List<SimpleItem> { return items } fun addItemCount(itemId: Int) { items.find { it.itemId == itemId }?.let { it.itemClickCount += 1 } }El repositorio de GitHub: https://github.com/ellasaro/ListAdapterTest
¡Salud!
No use clases de datos mutables o listas mutables con DiffUtil. Puede dar lugar a todo tipo de problemas. DiffUtil se basa en la comparación de dos listas, por lo que si una de ellas es mutable y se ha modificado, no puede comparar la antigua y la nueva con éxito porque no hay registro del estado anterior.
No me tomé el tiempo para reducir su problema exacto, pero apuesto a que si cambia getItems() de su Repo para devolver items.toList() (por lo que mutar el Repo no muta las listas descendentes), y cambia SimpleItem para que sea una clase inmutable, tus problemas desaparecerán.
Desafortunadamente, hacer que SimpleItem sea inmutable será un poco complicado. El oyente de clics en lugar de mutar el elemento tendrá que informar al repositorio la identificación del elemento que cambió, y el repositorio debe cambiarlo manualmente y luego actualizar la lista.
Será más limpio si su Repo devuelve un Flujo de listas que se emite automáticamente cuando se le informan cambios. Entonces, su ViewModel no tiene que informar los cambios y luego recordar consultar manualmente el estado de la lista nuevamente.
toList() y no toMutableList() . Una lista mutable comunica que planea mutar la lista en lugar de simplemente leerla, lo que nunca debe hacer con una lista que se pasa a un DiffUtil.
Declarar la propiedad itemClickCount como val y obtener la lista como una lista inmutable del objeto Repo hizo el truco como sugirió Tenfour04 .
Como observación adicional, si mantiene la propiedad itemClickCount como var pero reemplaza el elemento por completo y vuelve a enviar la lista actualizada, funciona correctamente. Entonces, el problema parece estar modificando la propiedad mutable del objeto directamente en la lista de Repo . Usar .toList() en getList() no ayudó en ese caso.