¿Cómo actualizar los datos del adaptador RecyclerView?

Durante 4 días no puedo entender cuál es exactamente el problema con la actualización del Adaptador de RecyclerView .

Después de obtener una nueva lista de productos, intenté:

  1. Actualice ArrayList desde el fragmento donde se crea recyclerView , establezca nuevos datos en el adaptador y luego llame a adapter.notifyDataSetChanged() ; No funcionó.

  2. Cree un nuevo adaptador, como otros lo hicieron, y funcionó para ellos, pero no recyclerView.setAdapter(new RecyclerViewAdapter(newArrayList)) para mí: recyclerView.setAdapter(new RecyclerViewAdapter(newArrayList))

  3. Cree un método en Adapter que actualice los datos de la siguiente manera:

     public void updateData(ArrayList viewModels) { items.clear(); items.addAll(viewModels); notifyDataSetChanged(); } 

    Luego llamo a este método cada vez que deseo actualizar la lista de datos; No funcionó.

  4. Para comprobar si puedo modificar el recyclerView de alguna manera, y traté de eliminar al menos un elemento:

      public void removeItem(int position) { items.remove(position); notifyItemRemoved(position); } 

    Todo permaneció como estaba.

Aquí está mi adaptador:

 public class RecyclerViewAdapter extends RecyclerView.Adapter implements View.OnClickListener { private ArrayList items; private OnItemClickListener onItemClickListener; public RecyclerViewAdapter(ArrayList items) { this.items = items; } public void setOnItemClickListener(OnItemClickListener onItemClickListener) { this.onItemClickListener = onItemClickListener; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false); v.setOnClickListener(this); return new ViewHolder(v); } public void updateData(ArrayList viewModels) { items.clear(); items.addAll(viewModels); notifyDataSetChanged(); } public void addItem(int position, ViewModel viewModel) { items.add(position, viewModel); notifyItemInserted(position); } public void removeItem(int position) { items.remove(position); notifyItemRemoved(position); } @Override public void onBindViewHolder(ViewHolder holder, int position) { ViewModel item = items.get(position); holder.title.setText(item.getTitle()); Picasso.with(holder.image.getContext()).load(item.getImage()).into(holder.image); holder.price.setText(item.getPrice()); holder.credit.setText(item.getCredit()); holder.description.setText(item.getDescription()); holder.itemView.setTag(item); } @Override public int getItemCount() { return items.size(); } @Override public void onClick(final View v) { // Give some time to the ripple to finish the effect if (onItemClickListener != null) { new Handler().postDelayed(new Runnable() { @Override public void run() { onItemClickListener.onItemClick(v, (ViewModel) v.getTag()); } }, 0); } } protected static class ViewHolder extends RecyclerView.ViewHolder { public ImageView image; public TextView price, credit, title, description; public ViewHolder(View itemView) { super(itemView); image = (ImageView) itemView.findViewById(R.id.image); price = (TextView) itemView.findViewById(R.id.price); credit = (TextView) itemView.findViewById(R.id.credit); title = (TextView) itemView.findViewById(R.id.title); description = (TextView) itemView.findViewById(R.id.description); } } public interface OnItemClickListener { void onItemClick(View view, ViewModel viewModel); } } 

E inicio RecyclerView siguiente manera:

 recyclerView = (RecyclerView) view.findViewById(R.id.recycler); recyclerView.setLayoutManager(new GridLayoutManager(getActivity(), 5)); adapter = new RecyclerViewAdapter(items); adapter.setOnItemClickListener(this); recyclerView.setAdapter(adapter); 

Entonces, ¿cómo realmente actualizo los datos del adaptador para mostrar los artículos recién recibidos?


Actualización: el problema era que el diseño donde gridView se veía de la siguiente manera:

        

Luego, LinearLayout e hice FrameLayout como diseño principal.

Estoy trabajando con RecyclerView y tanto la eliminación y la actualización funcionan bien.

1) ELIMINAR: hay 4 pasos para eliminar un elemento de un RecyclerView

 list.remove(position); recycler.removeViewAt(position); mAdapter.notifyItemRemoved(position); mAdapter.notifyItemRangeChanged(position, list.size()); 

Esta línea de códigos funciona para mí.

2) ACTUALIZAR LOS DATOS: Lo único que tuve que hacer es

 mAdapter.notifyDataSetChanged(); 

Debes hacer todo esto en el código de Actvity / Fragment que no está en el código del RecyclerView Adapter.

Esta es una respuesta general para futuros visitantes. Se explican las diversas formas de actualizar los datos del adaptador. El proceso incluye dos pasos principales cada vez:

  1. Actualiza el conjunto de datos
  2. Notificar al adaptador del cambio

Insertar un solo elemento

Agrega “Cerdo” en el índice 2 .

Insertar un solo elemento

 String item = "Pig"; int insertIndex = 2; data.add(insertIndex, item); adapter.notifyItemInserted(insertIndex); 

Insertar varios elementos

Inserta tres animales más en el índice 2 .

Insertar varios elementos

 ArrayList items = new ArrayList<>(); items.add("Pig"); items.add("Chicken"); items.add("Dog"); int insertIndex = 2; data.addAll(insertIndex, items); adapter.notifyItemRangeInserted(insertIndex, items.size()); 

Eliminar un solo artículo

Retire “Cerdo” de la lista.

Eliminar un solo artículo

 int removeIndex = 2; data.remove(removeIndex); adapter.notifyItemRemoved(removeIndex); 

Eliminar varios elementos

Retire “Camel” y “Sheep” de la lista.

Eliminar varios elementos

 int startIndex = 2; // inclusive int endIndex = 4; // exclusive int count = endIndex - startIndex; // 2 items will be removed data.subList(startIndex, endIndex).clear(); adapter.notifyItemRangeRemoved(startIndex, count); 

Eliminar todos los elementos

Borrar toda la lista

Eliminar todos los elementos

 data.clear(); adapter.notifyDataSetChanged(); 

Reemplazar la lista anterior con una nueva lista

Borre la lista anterior y luego agregue una nueva.

Reemplazar la lista anterior con una nueva lista

 // clear old list data.clear(); // add new list ArrayList newList = new ArrayList<>(); newList.add("Lion"); newList.add("Wolf"); newList.add("Bear"); data.addAll(newList); // notify adapter adapter.notifyDataSetChanged(); 

El adapter tiene una referencia a los data , por lo que es importante que no haya establecido data para un nuevo objeto. En su lugar, borré los elementos antiguos de los data y luego agregué los nuevos.

Actualizar artículo individual

Cambie el elemento “Ovejas” para que diga “Me gustan las ovejas”.

Actualizar artículo individual

 String newValue = "I like sheep."; int updateIndex = 3; data.set(updateIndex, newValue); adapter.notifyItemChanged(updateIndex); 

Mover solo elemento

Mueva “Oveja” desde la posición 3 a la posición 1 .

Mover solo elemento

 int fromPosition = 3; int toPosition = 1; // update data array String item = data.get(fromPosition); data.remove(fromPosition); data.add(toPosition, item); // notify adapter adapter.notifyItemMoved(fromPosition, toPosition); 

Código

Aquí está el código del proyecto para su referencia. El código del adaptador RecyclerView se puede encontrar en esta respuesta .

MainActivity.java

 public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener { List data; MyRecyclerViewAdapter adapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // data to populate the RecyclerView with data = new ArrayList<>(); data.add("Horse"); data.add("Cow"); data.add("Camel"); data.add("Sheep"); data.add("Goat"); // set up the RecyclerView RecyclerView recyclerView = findViewById(R.id.rvAnimals); LinearLayoutManager layoutManager = new LinearLayoutManager(this); recyclerView.setLayoutManager(layoutManager); DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(), layoutManager.getOrientation()); recyclerView.addItemDecoration(dividerItemDecoration); adapter = new MyRecyclerViewAdapter(this, data); adapter.setClickListener(this); recyclerView.setAdapter(adapter); } @Override public void onItemClick(View view, int position) { Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on row number " + position, Toast.LENGTH_SHORT).show(); } public void onButtonClick(View view) { insertSingleItem(); } private void insertSingleItem() { String item = "Pig"; int insertIndex = 2; data.add(insertIndex, item); adapter.notifyItemInserted(insertIndex); } private void insertMultipleItems() { ArrayList items = new ArrayList<>(); items.add("Pig"); items.add("Chicken"); items.add("Dog"); int insertIndex = 2; data.addAll(insertIndex, items); adapter.notifyItemRangeInserted(insertIndex, items.size()); } private void removeSingleItem() { int removeIndex = 2; data.remove(removeIndex); adapter.notifyItemRemoved(removeIndex); } private void removeMultipleItems() { int startIndex = 2; // inclusive int endIndex = 4; // exclusive int count = endIndex - startIndex; // 2 items will be removed data.subList(startIndex, endIndex).clear(); adapter.notifyItemRangeRemoved(startIndex, count); } private void removeAllItems() { data.clear(); adapter.notifyDataSetChanged(); } private void replaceOldListWithNewList() { // clear old list data.clear(); // add new list ArrayList newList = new ArrayList<>(); newList.add("Lion"); newList.add("Wolf"); newList.add("Bear"); data.addAll(newList); // notify adapter adapter.notifyDataSetChanged(); } private void updateSingleItem() { String newValue = "I like sheep."; int updateIndex = 3; data.set(updateIndex, newValue); adapter.notifyItemChanged(updateIndex); } private void moveSingleItem() { int fromPosition = 3; int toPosition = 1; // update data array String item = data.get(fromPosition); data.remove(fromPosition); data.add(toPosition, item); // notify adapter adapter.notifyItemMoved(fromPosition, toPosition); } } 

Notas

  • Si usa notifyDataSetChanged() , entonces no se realizará ninguna animación. Esto también puede ser una operación costosa, por lo que no se recomienda utilizar notifyDataSetChanged() si solo está actualizando un solo elemento o un rango de elementos.
  • Consulte DiffUtil si realiza cambios grandes o complejos en una lista.

Estudio adicional

  • CodePath: Uso de RecyclerView
  • Manera inteligente de actualizar RecyclerView usando DiffUtil

Esto es lo que funcionó para mí:

 recyclerView.setAdapter(new RecyclerViewAdapter(newList)); recyclerView.invalidate(); 

Después de crear un nuevo adaptador que contiene la lista actualizada (en mi caso fue una base de datos convertida en ArrayList) y configurarlo como adaptador, probé recyclerView.invalidate() y funcionó.

tiene 2 opciones para hacer esto: actualizar la interfaz de usuario del adaptador:

 mAdapter.notifyDataSetChanged(); 

o actualizar desde recyclerView:

 recyclerView.invalidate(); 

Actualizar datos de listview, gridview y recyclerview

 mAdapter.notifyDataSetChanged(); 

o

 mAdapter.notifyItemRangeChanged(0, itemList.size()); 

Otra opción es usar diffutil . Comparará la lista original con la nueva lista y usará la nueva lista como la actualización si hay un cambio.

Básicamente, podemos usar DiffUtil para comparar los datos antiguos con los nuevos y dejar que llame a notifyItemRangeRemoved, notifyItemRangeChanged y notifyItemRangeInserted en su nombre.

Un ejemplo rápido de uso de diffUtil en lugar de notifyDataSetChanged:

 DiffResult diffResult = DiffUtil .calculateDiff(new MyDiffUtilCB(getItems(), items)); //any clear up on memory here and then diffResult.dispatchUpdatesTo(this); 

Hago el trabajo de CalclateDiff fuera del hilo principal en caso de que sea una gran lista.

He resuelto el mismo problema de otra manera. No tengo datos. Lo espero del hilo de fondo, así que empiece con una lista emty.

  mAdapter = new ModelAdapter(getContext(),new ArrayList()); // then when i get data mAdapter.update(response.getModelList()); // and update is in my adapter public void update(ArrayList modelList){ adapterModelList.clear(); for (Product model: modelList) { adapterModelList.add(model); } mAdapter.notifyDataSetChanged(); } 

Eso es.

La mejor y más genial forma de agregar nuevos datos a los datos actuales es

  ArrayList newItems = new ArrayList(); newItems = getList(); int oldListItemscount = alcontainerDetails.size(); alcontainerDetails.addAll(newItems); recyclerview.getAdapter().notifyItemChanged(oldListItemscount+1, al_containerDetails); 

Descubrí que una forma realmente simple de volver a cargar RecyclerView es simplemente llamar

 recyclerView.removeAllViews(); 

Esto primero eliminará todo el contenido de RecyclerView y luego lo agregará nuevamente con los valores actualizados.

tengo la respuesta después de un largo tiempo

  SELECTEDROW.add(dt); notifyItemInserted(position); SELECTEDROW.remove(position); notifyItemRemoved(position); 

Si nada de lo mencionado en los comentarios anteriores funciona para usted. Puede significar que el problema está en otro lugar.

Un lugar en el que encontré la solución fue en la forma en que estaba configurando la lista para el adaptador. En mi actividad, la lista era una variable de instancia y la estaba cambiando directamente cuando cambiaba cualquier dato. Debido a que era una variable de referencia, sucedía algo raro. Así que cambié la variable de referencia por una local y usé otra variable para actualizar los datos y luego addAll() a la función addAll() mencionada en las respuestas anteriores.

Estos métodos son eficientes y buenos para comenzar a usar un RecyclerView básico.

 private List items; public void setItems(List newItems) { clearItems(); addItems(newItems); } public void addItem(YourItem item, int position) { if (position > items.size()) return; items.add(item); notifyItemInserted(position); } public void addMoreItems(List newItems) { int position = items.size() + 1; newItems.addAll(newItems); notifyItemChanged(position, newItems); } public void addItems(List newItems) { items.addAll(newItems); notifyDataSetChanged(); } public void clearItems() { items.clear(); notifyDataSetChanged(); } public void addLoader() { items.add(null); notifyItemInserted(items.size() - 1); } public void removeLoader() { items.remove(items.size() - 1); notifyItemRemoved(items.size()); } public void removeItem(int position) { if (position >= items.size()) return; items.remove(position); notifyItemRemoved(position); } public void swapItems(int positionA, int positionB) { if (positionA > items.size()) return; if (positionB > items.size()) return; YourItem firstItem = items.get(positionA); videoList.set(positionA, items.get(positionB)); videoList.set(positionB, firstItem); notifyDataSetChanged(); } 

Puede implementarlos dentro de una Clase de adaptador o en su Fragmento o Actividad, pero en ese caso debe crear una instancia del Adaptador para llamar a los métodos de notificación. En mi caso, generalmente lo implemento en el Adaptador.

Veo todos estos métodos que toman tiempo, encontré un camino alternativo.

Acabo de incluir un Intento de sí mismo y un final llamado, esto asegura que la lista se actualiza.

 Intent intent = new Intent(context,thisActivity.class); startActivity(intent); finish(); 

Hecho !