Comment mettre à jour / actualiser un élément spécifique dans RecyclerView

112

J'essaie d'actualiser un élément spécifique dans RecyclerView.

Histoire: chaque fois que l'utilisateur clique sur l'élément, cela s'affiche AlertDialog. L'utilisateur peut taper du texte en cliquant sur le bouton OK. Je veux afficher ce texte dans cet élément et afficher invisible ImageView- déclaré en XML et adaptateur ViewHolder-

J'ai utilisé cette fonction dans AlertDialogPositive Button pour mettre à jour l'élément:

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);
}

Mais ce code modifie non seulement le itemView à la position passée, mais modifie également certains des autres itemView!

Comment dois-je modifier correctement un itemView spécifique en cliquant dessus?

Elgendy
la source

Réponses:

98

Vous pouvez utiliser la notifyItemChanged(int position)méthode de la classe RecyclerView.Adapter. De la documentation:

Avertissez tous les observateurs enregistrés que l'élément en position a changé. Équivaut à appeler notifyItemChanged (position, null) ;.

Il s'agit d'un événement de changement d'article, pas d'un événement de changement structurel. Il indique que tout reflet des données à la position est obsolète et doit être mis à jour. L'élément en position conserve la même identité.

Comme vous avez déjà le poste, cela devrait fonctionner pour vous.

Edson Menegatti
la source
9
Je l'ai déjà utilisé mais si je veux mettre à jour la position 0, il met à jour la position 0, 9, 19, ...
Elgendy
Désolé, je n'ai pas compris votre commentaire. Quelle position est-elle mise à jour? 0, 9 ou la plage entre 0 et 9?
Edson Menegatti
Non, ce n'est pas une plage, chaque fois que je mets à jour un élément, cela rafraîchit également les éléments indésirables
Elgendy
@Learner Lorsque vous mettez à jour le ViewHolder à la position 0, il reste à jour. Le RecyclerView recyclera ce ViewHolder et l'utilisera pour la position 9, de sorte que vous y voyez ces modifications. Je recommande de définir ce texte dans onBindViewHolder () à la place - de cette façon, chaque fois qu'un ViewHolder est recyclé, vous pouvez vérifier sa position et définir le texte ou rendre l'image visible ou quoi que ce soit correctement.
Cliabhach
@Cliabhach toujours le même problème en faisant des changementsonBindViewHolder()
Choletski
39

Mettre à jour un seul élément

  1. Mettre à jour l'élément de données
  2. Informez l'adaptateur du changement avec notifyItemChanged(updateIndex)

Exemple

Modifiez l'élément "Moutons" pour qu'il dise "J'aime les moutons".

Mettre à jour un seul élément

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

Ma réponse complète avec plus d'exemples est ici .

Suragch
la source
comment faire si je veux que les données mises à jour basculent vers le haut
Choy
@Choy, après avoir mis à jour les données, vous pouvez les déplacer vers l'index 0. Voir la section Déplacer un seul élément de cet élément
Suragch
11

Ajoutez le texte modifié à votre liste de données de modèle

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

Je pense que j'ai une idée sur la façon de gérer cela. La mise à jour est identique à la suppression et au remplacement à la position exacte. Je supprime donc d'abord l'élément de cette position en utilisant le code ci-dessous:

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

puis j'ajouterais l'élément à cette position particulière, comme indiqué ci-dessous:

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

J'essaye d'implémenter ceci maintenant. Je vous ferai part de vos commentaires lorsque j'aurai terminé!

EGBENCHONG II J. Ayuk
la source
2
Vous faites une étape supplémentaire. Remplacez simplement l'élément à la position et appelez l'élément notifyItemChanged (position);
toshkinl
@toshkinl Comment sauriez-vous que les données proviennent de JSONObject? sans position?
olajide
7

Je dois résoudre ce problème en saisissant la position de l'élément à modifier, puis dans l'appel de l'adaptateur

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

, cela appellera onBindViewHolder (titulaire ViewHolder, position int) pour cet élément spécifique à cette position spécifique.

haneenCo
la source
2

La solution ci-dessous a fonctionné pour moi:

Sur un élément RecyclerView, l'utilisateur cliquera sur un bouton mais une autre vue comme TextView se mettra à jour sans notifier directement l'adaptateur:

J'ai trouvé une bonne solution pour cela sans utiliser la méthode notifyDataSetChanged (), cette méthode recharge toutes les données de recyclerView donc si vous avez une image ou une vidéo à l'intérieur de l'élément, elles se rechargeront et l'expérience utilisateur ne sera pas bonne:

Voici un exemple de clic sur une icône de type ImageView et ne met à jour qu'un seul TextView (possibilité de mettre à jour plus de vue de la même manière pour le même élément) pour afficher la mise à jour du comptage après l'ajout de +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
la source
1

Une façon qui a fonctionné pour moi personnellement, consiste à utiliser les méthodes d'adaptateur de recyclerview pour gérer les changements dans ses éléments.

Cela irait d'une manière similaire à ceci, créez une méthode dans la vue de votre recycleur personnalisé un peu comme ceci:

public void modifyItem(final int position, final Model model) {
    mainModel.set(position, model);
    notifyItemChanged(position);
}
user3709410
la source
0

Dans votre classe d'adaptateur, dans la méthode onBindViewHolder, définissez ViewHolder sur setIsRecyclable (false) comme dans le code ci-dessous.

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

    // Then your other codes
}
shankar_vl
la source
0

il vous suffit d'ajouter le code suivant dans la boîte de dialogue d'alerte lors de l'enregistrement, cliquez sur

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

Le problème est RecyclerView.Adatperque ne fournit aucune méthode qui renvoie l'index de l'élément

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);
}

Ma solution de contournement consiste à créer une instance de carte pour (élément, position) 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
}
  • le type d'élément (ici classe Friend) doit implémenter hashCode()et equals()parce qu'il est clé dans hashmap.

quand un élément a changé,

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

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

  }

Il est bon de définir une méthode d'assistance

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

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

L'instance de carte ( Map<Friend, Integer> posMap) n'est pas nécessairement requise. Si la carte n'est pas utilisée, une boucle dans toute la liste peut trouver la position d'un élément.

chmin
la source
-1

C'est aussi mon dernier problème. Voici ma solution J'utilise le modèle de données et l'adaptateur pour mon 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
la source
-1

si vous créez un objet et l'ajoutez à la liste que vous utilisez dans votre adaptateur, lorsque vous modifiez un élément de votre liste dans l'adaptateur, tous vos autres éléments changent également, en d'autres termes, tout est question de références et votre liste ne change pas détenez des copies séparées de cet objet unique.

A.sobhdel
la source
-5

Dans votre adaptateur RecyclerView, vous devez avoir un ArrayList et également une méthode addItemsToList(items)pour ajouter des éléments de liste à ArrayList. Ensuite, vous pouvez ajouter des éléments de liste par appel de adapter.addItemsToList(items)manière dynamique. Une fois tous vos éléments de liste ajoutés à ArrayList, vous pouvez appeler adapter.notifyDataSetChanged()pour afficher votre liste.

Vous pouvez utiliser le notifyDataSetChangeddans l'adaptateur pour RecyclerView

Nooruddin Lakhani
la source
oui j'ai ArrayList pour les données, cela fonctionne sans aucun problème, mon problème de mise à jour d'un élément spécifique -existant- dans cette liste
Elgendy
vous devriez avoir la position de l'élément qui se met à jour, il vous suffit de mettre un nouvel élément à sa position respective comme mList.add (position) .setName ("nouveau nom"); puis utilisez mAdapter.notifyDataSetChanged ();
Nooruddin Lakhani
4
Cette réponse ignore le désir de mettre à jour un seul élément. L'appel de notifyDataSetChanged () ne doit être effectué que lorsque l'ensemble de données change.
Mark McClelland