Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

2.4K
Vistas
ListAdapter no actualiza el elemento en RecyclerView

Estoy usando la nueva biblioteca de soporte ListAdapter . Aquí está mi código para el adaptador.

 class ArtistsAdapter : ListAdapter<Artist, ArtistsAdapter.ViewHolder>(ArtistsDiff()) { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { return ViewHolder(parent.inflate(R.layout.item_artist)) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(getItem(position)) } class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { fun bind(artist: Artist) { itemView.artistDetails.text = artist.artistAlbums .plus(" Albums") .plus(" \u2022 ") .plus(artist.artistTracks) .plus(" Tracks") itemView.artistName.text = artist.artistCover itemView.artistCoverImage.loadURL(artist.artistCover) } } }

Estoy actualizando el adaptador con

 musicViewModel.getAllArtists().observe(this, Observer { it?.let { artistAdapter.submitList(it) } })

mi clase diferencial

 class ArtistsDiff : DiffUtil.ItemCallback<Artist>() { override fun areItemsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem?.artistId == newItem?.artistId } override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem == newItem } }

Lo que sucede es que cuando se llama a submitList la primera vez que el adaptador representa todos los elementos, pero cuando se vuelve a llamar a submitList con propiedades de objeto actualizadas, no se vuelve a representar la vista que ha cambiado.

Vuelve a representar la vista a medida que me desplazo por la lista, que a su vez llama a bindView()

Además, me di cuenta de que llamar a adapter.notifyDatasSetChanged() después de enviar la lista muestra la vista con valores actualizados, pero no quiero llamar a notifyDataSetChanged() porque el adaptador de lista tiene funciones de diferenciación integradas.

¿Me puede ayudar alguien?

over 4 years ago · Santiago Trujillo
26 Respuestas
Responde la pregunta

0

Si encuentra algunos problemas al usar

recycler_view.setHasFixedSize(true)

definitivamente deberías revisar este comentario: https://github.com/thoughtbot/expandable-recycler-view/issues/53#issuecomment-362991531

Resolvió el problema de mi lado.

(Aquí hay una captura de pantalla del comentario solicitado)

ingrese la descripción de la imagen aquí

over 4 years ago · Santiago Trujillo Denunciar

0

Hoy también me topé con este "problema". Con la ayuda de la respuesta de insa_c y la solución de RJFares, me hice una función de extensión de Kotlin:

 /** * Update the [RecyclerView]'s [ListAdapter] with the provided list of items. * * Originally, [ListAdapter] will not update the view if the provided list is the same as * currently loaded one. This is by design as otherwise the provided DiffUtil.ItemCallback<T> * could never work - the [ListAdapter] must have the previous list if items to compare new * ones to using provided diff callback. * However, it's very convenient to call [ListAdapter.submitList] with the same list and expect * the view to be updated. This extension function handles this case by making a copy of the * list if the provided list is the same instance as currently loaded one. * * For more info see 'RJFares' and 'insa_c' answers on * https://stackoverflow.com/questions/49726385/listadapter-not-updating-item-in-reyclerview */ fun <T, VH : RecyclerView.ViewHolder> ListAdapter<T, VH>.updateList(list: List<T>?) { // ListAdapter<>.submitList() contains (stripped): // if (newList == mList) { // // nothing to do // return; // } this.submitList(if (list == this.currentList) list.toList() else list) }

que luego se puede usar en cualquier lugar, por ejemplo:

 viewModel.foundDevices.observe(this, Observer { binding.recyclerViewDevices.adapter.updateList(it) })

y solo (y siempre) copia la lista si es la misma que está cargada actualmente.

over 4 years ago · Santiago Trujillo Denunciar

0

con Kotlin solo necesita convertir su lista a una nueva MutableList como esta u otro tipo de lista según su uso

 .observe(this, Observer { adapter.submitList(it?.toMutableList()) })
over 4 years ago · Santiago Trujillo Denunciar

0

Para mí, este problema apareció si estaba usando RecyclerView dentro de ScrollView con nestedScrollingEnabled="false" y la altura de RV establecida en wrap_content .
El adaptador se actualizó correctamente y se llamó a la función de vinculación, pero no se mostraron los elementos: el RecyclerView estaba atascado en su tamaño original.

Cambiar ScrollView a NestedScrollView solucionó el problema.

over 4 years ago · Santiago Trujillo Denunciar

0

Para cualquiera que tenga el mismo escenario que el mío, dejo mi solución, que no sé por qué funciona, aquí.

La solución que funcionó para mí fue de @Mina Samir, que envía la lista como una lista mutable.

Mi escenario de problema:

-Cargar una lista de amigos dentro de un fragmento.

  1. ActivityMain adjunta FragmentFriendList (observa los datos en vivo de los elementos de la base de datos de amigos) y, al mismo tiempo, solicita una solicitud http al servidor para obtener toda mi lista de amigos.

  2. Actualice o inserte los elementos del servidor http.

  3. Cada cambio enciende la devolución de llamada onChanged de livedata. Pero, cuando es la primera vez que inicio la aplicación, lo que significa que no había nada en mi mesa, la lista de envío se realiza correctamente sin ningún tipo de error, pero no aparece nada en la pantalla.

  4. Sin embargo, cuando es la segunda vez que inicio la aplicación, los datos se cargan en la pantalla.

La solución es, como se mencionó anteriormente, enviar la lista como mutableList.

over 4 years ago · Santiago Trujillo Denunciar

0

El uso de la primera respuesta de @RJFares actualiza la lista correctamente, pero no mantiene el estado de desplazamiento. Todo el RecyclerView comienza desde la posición 0. Como solución, esto es lo que hice:

 fun updateDataList(newList:List<String>){ //new list from DB or Network val tempList = dataList.toMutableList() // dataList is the old list tempList.addAll(newList) listAdapter.submitList(tempList) // Recyclerview Adapter Instance dataList = tempList }

De esta forma, puedo mantener el estado de desplazamiento de RecyclerView junto con los datos modificados.

over 4 years ago · Santiago Trujillo Denunciar

0

En mi caso, olvidé configurar LayoutManager para RecyclerView . El efecto de eso es el mismo que el descrito anteriormente.

over 4 years ago · Santiago Trujillo Denunciar

0

Necesitaba modificar mis DiffUtils

override fun areContentsTheSame(oldItem: Vehicle, newItem: Vehicle): Boolean {

Para devolver realmente si los contenidos son nuevos, no solo compare la identificación del modelo.

over 4 years ago · Santiago Trujillo Denunciar

0

Tuve un problema similar. El problema estaba en las funciones Diff , que no comparaban adecuadamente los elementos. Cualquiera que tenga este problema, asegúrese de que sus funciones Diff (y, por extensión, sus clases de objetos de datos) contengan definiciones de comparación adecuadas, es decir, comparar todos los campos que podrían actualizarse en el nuevo elemento. Por ejemplo en la publicación original.

 override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem == newItem }

Esta función (potencialmente) no hace lo que dice en la etiqueta: no compara el contenido de los dos elementos, a menos que haya anulado la función equals() en la clase Artist . En mi caso no lo tenía, y la definición de areContentsTheSame solo marcaba uno de los campos necesarios, por mi descuido al implementarlo. Esto es igualdad estructural vs. igualdad referencial, puedes encontrar más sobre esto aquí

over 4 years ago · Santiago Trujillo Denunciar

0

También me encontré con un problema similar, mi caso de uso era que tenía un controlador de clics y el elemento se seleccionará/no seleccionará (alternar al hacer clic).

Probé la mayor parte del enfoque de las respuestas anteriores, lo único que funcionó es

 adapter.submitList(null) adapter.submitList(modifiedList)

pero el problema con esto es que cada vez que hago clic en cualquier controlador de clics, la lista completa se vuelve a dibujar, lo cual es muy ineficiente.

Lo que hice ?

Hice datos en vivo que almacenarán el último elemento en el que se hizo clic y observando esos datos en vivo, podemos decirle al adaptador que los datos en vivo se han actualizado como se muestra a continuación

 viewModel.lastClicked.observe(viewLifeCycleOwner, { adapter.notifyItemChanged(it) }
over 4 years ago · Santiago Trujillo Denunciar

0

ingrese la descripción de la imagen aquí

esto funcionará ... lo que sucede es que cuando obtiene la lista actual, está apuntando a la misma lista en la misma ubicación

over 4 years ago · Santiago Trujillo Denunciar

0

Una vez que haya modificado la lista de matrices, debe informar al adaptador qué posición debe cambiarse

este código a continuación está funcionando en mi caso, deseo que pueda ayudar

 private fun addItem() { val index = myArrayList.size val position = myArrayList.size+1 myArrayList.add( index, MyArrayClass("1", "Item Name") ) myAdapter.notifyItemInserted(position) // in case of insert // in case of remove item // val index = myArrayList.size-1 // myAdapter.notifyItemRemoved(index) }
over 4 years ago · Santiago Trujillo Denunciar

0

Tengo un comportamiento extraño. Estoy usando MutableList en LiveDate.

En kotlin, los siguientes códigos no funcionan:

 mViewModel.products.observe(viewLifecycleOwner, { mAdapter.submitList(it) })

Pero, cuando lo cambio a it.toList(), funciona

 mViewModel.products.observe(viewLifecycleOwner, { mAdapter.submitList(it.toList()) })

Aunque, "eso" era la misma lista.

over 4 years ago · Santiago Trujillo Denunciar

0

Tuve un problema MUY similar a este y decidí abrir un nuevo hilo e incluso crear un proyecto de GitHub para jugar. La mayoría de las soluciones no funcionaron para mí, ni siquiera la forma toMutableList() . En mi caso, el problema se resolvió usando clases inmutables y enviando List inmutables al Adaptador.

over 4 years ago · Santiago Trujillo Denunciar

0

En realidad, confundió las funciones de anulación en su clase ArtistsDiff

En lugar de esto

 class ArtistsDiff : DiffUtil.ItemCallback<Artist>() { override fun areItemsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem?.artistId == newItem?.artistId } override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem == newItem } }

Utilizar esta

 class ArtistsDiff : DiffUtil.ItemCallback<Artist>() { override fun areItemsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem == newItem // remove artistId } override fun areContentsTheSame(oldItem: Artist?, newItem: Artist?): Boolean { return oldItem?.artistId == newItem?.artistId // add artistId } }

Consulte también esta publicación para obtener más información sobre cómo usar ListAdapter.

over 4 years ago · Santiago Trujillo Denunciar

0

Perdió tanto tiempo para resolver el problema en el mismo caso.

Pero en mi situación, el problema fue que olvidé especificar un layoutManager para mi recyclerView: vRecyclerView.layoutManager = LinearLayoutManager(requireContext())

Espero que nadie repita mi error...

over 4 years ago · Santiago Trujillo Denunciar

0

La razón por la que no se llama a su ListAdapter .submitlist es porque el objeto que actualizó aún tiene la misma dirección en la memoria.

Cuando actualiza un objeto con digamos .setText, cambia el valor en el objeto original.

De modo que cuando verifique si object.id == object2.id, volverá igual porque ambos tienen una referencia a la misma ubicación en la memoria.

La solución es crear un nuevo objeto con los datos actualizados e insertarlo en su lista. Luego se llamará a submitList y funcionará correctamente.

over 4 years ago · Santiago Trujillo Denunciar

0

Solución óptima: para Kotlin

 var list :ArrayList<BaseModel> = ArrayList(adapter.currentList) list.add(Item("Content")) adapter.submitList(list) { Log.e("ListAdaptor","List Updated Successfully") }

No debemos mantener otra lista base, ya que adapter.currentList devolverá una lista en la que ya se calculó la diferencia.

Tenemos que proporcionar una nueva instancia cada vez que se actualiza una lista debido a DiffUtil. Según la documentación de Android, DiffUtil es una clase de utilidad que calcula la diferencia entre dos listas y genera una lista de operaciones de actualización que convierte la primera lista en la segunda. AsyncListDiffer ya mantiene una lista que ejecuta el diffutil en el subproceso de fondo y otra debe pasarse usando adaptor.submitList()

over 4 years ago · Santiago Trujillo Denunciar

0

Resuelve mi problema. Creo que la mejor manera es no anular la lista de submitList sino agregar una nueva función para agregar una nueva lista.

 fun updateList(list: MutableList<ScaleDispBlock>?) { list?.let { val newList = ArrayList<ScaleDispBlock>(list) submitList(newList) } }
over 4 years ago · Santiago Trujillo Denunciar

0

Como ya se mencionó, no puede enviar una Lista con la misma referencia porque ListAdapter verá que las listas están en la misma ubicación y, por lo tanto, no podrá usar DiffUtil.

La solución más sencilla sería hacer una copia superficial de la lista.

 submitList(ArrayList(list))

Tenga cuidado al convertir la lista en MutableList, ya que eso puede crear condiciones para excepciones y errores difíciles de encontrar.

over 4 years ago · Santiago Trujillo Denunciar

0

La forma en que funcionó para mí es anular la lista de submitList() y crear una copia de la lista entrante y cada elemento dentro de ella también:

 override fun submitList(list: List<Item>?) { val listCopy = mutableListOf<Item>().apply { list?.map { add(Item(it.id, it.name, it.imageUrl)) } } super.submitList(listCopy) }
over 4 years ago · Santiago Trujillo Denunciar

0

Me encuentro con un problema muy similar.

Después de cambiar la lista de datos, la envío de nuevo, la vista del reciclador no se muestra como yo quería. Muestra elementos duplicados.

No he encontrado la causa raíz, pero encuentro una solución alternativa, que es configurar el adaptador en la vista de reciclador nuevamente. Supongo que esto hace que el visor del reciclador olvide la memoria anterior y vuelva a renderizar correctamente.

 userNftListFiltered = SOME_NEW_VALUE binding.nftSendSearchList.adapter = searchNftAdapter //set adapter again searchNftAdapter.submitList(userNftListFiltered)
over 4 years ago · Santiago Trujillo Denunciar

0

Tuve un problema similar, pero la representación incorrecta se debió a una combinación de setHasFixedSize(true) y android:layout_height="wrap_content" . Por primera vez, el adaptador se suministró con una lista vacía, por lo que la altura nunca se actualizó y era 0 . De todos modos, esto resolvió mi problema. Alguien más podría tener el mismo problema y pensará que es un problema con el adaptador.

over 4 years ago · Santiago Trujillo Denunciar

0

Según los documentos oficiales:

Cada vez que llama a submitList , envía una nueva lista para que se diferencie y se muestre.

Esta es la razón por la que cada vez que llama a submitList en la lista anterior (ya enviada), no calcula la diferencia y no notifica al adaptador para cambios en el conjunto de datos.

over 4 years ago · Santiago Trujillo Denunciar

0

Editar: entiendo por qué sucede esto, ese no era mi punto. Mi punto es que al menos necesita dar una advertencia o llamar a la función notifyDataSetChanged() . Porque aparentemente estoy llamando a la submitList(...) por una razón. Estoy bastante seguro de que la gente está tratando de averiguar qué salió mal durante horas hasta que se dan cuenta de que la lista de envío () ignora la llamada en silencio.

Esto se debe a la extraña lógica de Google . Entonces, si pasa la misma lista al adaptador, ni siquiera llama a DiffUtil .

 public void submitList(final List<T> newList) { if (newList == mList) { // nothing to do return; } .... }

Realmente no entiendo todo el punto de este ListAdapter si no puede manejar cambios en la misma lista. Si desea cambiar los elementos en la lista, pase a ListAdapter y vea los cambios, entonces necesita crear una copia profunda de la lista o necesita usar RecyclerView normal con su propia clase DiffUtill .

over 4 years ago · Santiago Trujillo Denunciar

0

La biblioteca asume que está utilizando Room o cualquier otro ORM que ofrece una nueva lista asíncrona cada vez que se actualiza, por lo que simplemente llamar a submitList funcionará, y para los desarrolladores descuidados, evita hacer los cálculos dos veces si se llama a la misma lista.

La respuesta aceptada es correcta, ofrece la explicación pero no la solución.

Lo que puede hacer en caso de que no esté utilizando ninguna de esas bibliotecas es:

 submitList(null); submitList(myList);

Otra solución sería anular la lista de envío (que no causa ese parpadeo rápido) como tal:

 @Override public void submitList(final List<Author> list) { super.submitList(list != null ? new ArrayList<>(list) : null); }

O con código Kotlin:

 override fun submitList(list: List<CatItem>?) { super.submitList(list?.let { ArrayList(it) }) }

Lógica cuestionable pero funciona perfectamente. Mi método preferido es el segundo porque no hace que cada fila reciba una llamada onBind.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda