Cómo actualizar / actualizar un elemento específico en RecyclerView

112

Estoy intentando actualizar un elemento específico en RecyclerView.

Historia: siempre que el usuario hace clic en un elemento, se muestra AlertDialog. El usuario puede escribir texto haciendo clic en el botón Aceptar. Quiero mostrar este texto en este elemento y mostrar invisible ImageView- declarado en XML y adaptador ViewHolder-

Usé esta función en el AlertDialogbotón positivo para actualizar el artículo:

private void updateListItem(int position) {
  View view = layoutManager.findViewByPosition(position);
  ImageView medicineSelected = (ImageView) view.findViewById(R.id.medicine_selected);
  medicineSelected.setVisibility(View.VISIBLE);
  TextView orderQuantity = (TextView) view.findViewById(R.id.order_quantity);
  orderQuantity.setVisibility(View.VISIBLE);
  orderQuantity.setText(quantity + " packet added!");

  medicinesArrayAdapter.notifyItemChanged(position);
}

¡Pero este código no solo cambia el itemView en la posición pasada, sino que también cambia algunos de los otros itemView (s) también!

¿Cómo debo cambiar el itemView específico correctamente haciendo clic en él?

Elgendy
fuente

Respuestas:

98

Puede utilizar el notifyItemChanged(int position)método de la clase RecyclerView.Adapter. De la documentación:

Notifique a los observadores registrados que el artículo en la posición ha cambiado. Equivalente a llamar a notifyItemChanged (posición, nulo) ;.

Este es un evento de cambio de artículo, no un evento de cambio estructural. Indica que cualquier reflejo de los datos en la posición está desactualizado y debe actualizarse. El artículo en la posición conserva la misma identidad.

Como ya tiene el puesto, debería funcionar para usted.

Edson Menegatti
fuente
9
Ya lo usé pero si quiero actualizar la posición 0, está actualizando la posición 0, 9, 19, ...
Elgendy
Lo siento, no entendí tu comentario. ¿Qué posición está actualizando? 0, 9 o el rango entre 0 y 9?
Edson Menegatti
No, no es rango, cada vez que actualizo un elemento, también se actualizan los elementos no deseados
Elgendy
@Learner Cuando actualiza ViewHolder en la posición 0, permanece actualizado. RecyclerView reciclará ese ViewHolder y lo usará para la posición 9, por lo que verá esos cambios allí. Recomiendo configurar este texto en onBindViewHolder () en su lugar; de esa manera, cada vez que se recicla un ViewHolder, puede verificar su posición y establecer el texto o hacer que la imagen sea visible o lo que sea correctamente.
Cliabhach
@Cliabhach sigue siendo el mismo problema al hacer cambios enonBindViewHolder()
Choletski
39

Actualizar un solo elemento

  1. Actualizar el elemento de datos
  2. Notifique al adaptador del cambio con notifyItemChanged(updateIndex)

Ejemplo

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

Actualizar un solo elemento

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

Mi respuesta completa con más ejemplos está aquí .

Suragch
fuente
cómo hacer eso si quiero que los datos actualizados cambien a la parte superior
Choy
@Choy, después de actualizar los datos, puede moverlos al índice 0. Consulte la sección Mover elemento único de este elemento
Suragch
11

Agregue el texto modificado a la lista de datos de su modelo

mdata.get(position).setSuborderStatusId("5");
mdata.get(position).setSuborderStatus("cancelled");
notifyItemChanged(position);
Shamir Kp
fuente
10

Creo que tengo una idea sobre cómo lidiar con esto. Actualizar es lo mismo que eliminar y reemplazar en la posición exacta. Entonces, primero elimino el elemento de esa posición usando el siguiente código:

public void removeItem(int position){
    mData.remove(position);
    notifyItemRemoved(position);
    notifyItemRangeChanged(position, mData.size());
}

y luego agregaría el elemento en esa posición particular como se muestra a continuación:

public void addItem(int position, Landscape landscape){
    mData.add(position, landscape);
    notifyItemInserted(position);
    notifyItemRangeChanged(position, mData.size());
}

Estoy tratando de implementar esto ahora. ¡Te daré un comentario cuando termine!

EGBENCHONG II J. Ayuk
fuente
2
Estás dando un paso más. Simplemente reemplace el elemento en la posición y llame al elemento notifyItemChanged (position);
toshkinl
@toshkinl ¿Cómo sabría si los datos provienen de JSONObject? sin cargo?
olajide
7

Pude resolver este problema detectando la posición del elemento que necesitaba ser modificado y luego en la llamada del adaptador

public void refreshBlockOverlay(int position) {
    notifyItemChanged(position);
}

, esto llamará a onBindViewHolder (titular de ViewHolder, posición int) para este elemento específico en esta posición específica.

haneenCo
fuente
2

La siguiente solución funcionó para mí:

En un elemento de RecyclerView, el usuario hará clic en un botón, pero otra vista como TextView se actualizará sin notificar directamente al adaptador:

Encontré una buena solución para esto sin usar el método notifyDataSetChanged (), este método recarga todos los datos de recyclerView, por lo que si tiene una imagen o video dentro del elemento, se recargarán y la experiencia del usuario no será buena:

Aquí hay un ejemplo de hacer clic en un ícono similar a ImageView y solo actualizar un único TextView (es posible actualizar más vistas de la misma manera del mismo elemento) para mostrar la actualización de conteo después de agregar +1:

// View holder class inside adapter class
public class MyViewHolder extends RecyclerView.ViewHolder{

    ImageView imageViewLike;

    public MyViewHolder(View itemView) {
         super(itemView);

        imageViewLike = itemView.findViewById(R.id.imageViewLike);
        imageViewLike.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                int pos = getAdapterPosition(); // Get clicked item position
                TextView tv = v.getRootView().findViewById(R.id.textViewLikeCount); // Find textView of recyclerView item
                resultList.get(pos).setLike(resultList.get(pos).getLike() + 1); // Need to change data list to show updated data again after scrolling
                tv.setText(String.valueOf(resultList.get(pos).getLike())); // Set data to TextView from updated list
            }
        });
    }
Touhid
fuente
1

Una forma que me ha funcionado personalmente es usar los métodos de adaptador de la vista de reciclaje para hacer frente a los cambios en sus elementos.

Sería similar a esto, crear un método en la vista de su reciclador personalizado algo así:

public void modifyItem(final int position, final Model model) {
    mainModel.set(position, model);
    notifyItemChanged(position);
}
usuario3709410
fuente
0

En su clase de adaptador, en el método onBindViewHolder, configure ViewHolder en setIsRecyclable (falso) como se muestra en el siguiente código.

@Override
public void onBindViewHolder(RecyclerViewAdapter.ViewHolder p1, int p2)
{
    // TODO: Implement this method
    p1.setIsRecyclable(false);

    // Then your other codes
}
shankar_vl
fuente
0

solo tiene que agregar el siguiente código en el cuadro de diálogo de alerta al hacer clic en guardar

          recyclerData.add(position, updateText.getText().toString());
          recyclerAdapter.notifyItemChanged(position);
Dhruvisha
fuente
0

El problema es RecyclerView.Adatperque no proporciona ningún método que devuelva el índice del elemento.

public abstract static class Adapter<VH extends ViewHolder> {
  /**
   * returns index of the given element in adapter, return -1 if not exist
   */
  public int indexOf(Object elem);
}

Mi solución es crear una instancia de mapa para (elemento, posición) s

public class FriendAdapter extends RecyclerView.Adapter<MyViewHolder> {
  private Map<Friend, Integer> posMap ;
  private List<Friend> friends;

  public FriendAdapter(List<Friend> friends ) {
    this.friends = new ArrayList<>(friends);
    this.posMap = new HashMap<>();
    for(int i = 0; i < this.friends.size(); i++) {
      posMap.put(this.friends.get(i), i);
    }
  }

  public int indexOf(Friend friend) {
    Integer position = this.posMap.get(elem);
    return position == null ? -1 : position;
  }
  // skip other methods in class Adapter
}
  • el tipo de elemento (aquí clase Friend) debe implementarse hashCode()y equals()porque es clave en hashmap.

cuando un elemento cambia,

  void someMethod() {
    Friend friend = ...;
    friend.setPhoneNumber('xxxxx');

    int position = friendAdapter.indexOf(friend);
    friendAdapter.notifyItemChanged(position);

  }

Es bueno definir un método auxiliar

public class FriendAdapter extends extends RecyclerView.Adapter<MyViewHolder> {

  public void friendUpdated(Friend friend) {
    int position = this.indexOf(friend);
    this.notifyItemChanged(position);
  }
}

La instancia de mapa ( Map<Friend, Integer> posMap) no es necesariamente necesaria. Si no se usa map, recorrer la lista puede encontrar la posición de un elemento.

chmin
fuente
-1

Ese también es mi último problema. Aquí mi solución utilizo modelo de datos y adaptador para mi RecyclerView

 /*Firstly, register your new data to your model*/
 DataModel detail = new DataModel(id, name, sat, image);

 /*after that, use set to replace old value with the new one*/
 int index = 4;
 mData.set(index, detail);

 /*finally, refresh your adapter*/
 if(adapter!=null)
    adapter.notifyItemChanged(index);
deya tri
fuente
-1

si está creando un objeto y agregándolo a la lista que usa en su adaptador, cuando cambia un elemento de su lista en el adaptador, todos sus otros elementos también cambian, en otras palabras, se trata de referencias y su lista no Mantenga copias separadas de ese único objeto.

A.sobhdel
fuente
-5

En su adaptador RecyclerView, debe tener una ArrayList y también un método addItemsToList(items)para agregar elementos de lista a ArrayList. Luego, puede agregar elementos de la lista mediante una llamada de adapter.addItemsToList(items)forma dinámica. Después de que todos los elementos de su lista se hayan agregado a ArrayList, puede llamaradapter.notifyDataSetChanged() para mostrar su lista.

Puede usar el notifyDataSetChangeden el adaptador para RecyclerView

Nooruddin Lakhani
fuente
sí, tengo ArrayList para obtener datos, funciona sin ningún problema, mi problema al actualizar un elemento específico -existente- en esta lista
Elgendy
debe tener la posición del elemento que se está actualizando, entonces solo tiene que poner un nuevo elemento en su posición respectiva como mList.add (posición) .setName ("nuevo nombre"); luego use mAdapter.notifyDataSetChanged ();
Nooruddin Lakhani
4
Esta respuesta ignora el deseo de actualizar un solo elemento. La llamada a notifyDataSetChanged () solo debe realizarse cuando cambia todo el conjunto de datos.
Mark McClelland