CheckBox dans RecyclerView continue de vérifier différents éléments


93

Voici le XML pour mes articles dans le RecyclerView

<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:id="@+id/cvItems"
    android:layout_height="wrap_content"
    android:layout_width="fill_parent"
    android:layout_margin="2dp"
    card_view:cardElevation="0dp"
    card_view:contentPadding="0dp"
    card_view:cardBackgroundColor="#FFFFFF"
    >

    <LinearLayout
        android:orientation="horizontal"
        android:layout_height="fill_parent"
        android:layout_width="fill_parent">
        <TextView
            android:layout_width="0dip"
            android:layout_height="match_parent"
            android:layout_weight="0.8"
            android:id="@+id/tvContent"
            android:textSize="15dp"
            android:paddingLeft="5dp"
            android:paddingRight="5dp" />
        <CheckBox
            android:id="@+id/cbSelect"
            android:layout_width="0dip"
            android:layout_weight="0.2"
            android:layout_height="match_parent"
            android:button="@drawable/cb_checked"
            android:gravity="center_horizontal"
            android:textAlignment="center"
            android:layout_gravity="center_horizontal" />
    </LinearLayout>
</android.support.v7.widget.CardView>

Et voici l'adaptateur RecyclerView qui gonfle la mise en page ci-dessus pour chacun de ses éléments:

public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {

    private ArrayList<ObjectIncome> myItems = new ArrayList<>();

    public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
        try {
            mContext = context;
            myItems = getItems;
        }catch (Exception e){
            Log.e(FILE_NAME, "51: " + e.toString());
            e.printStackTrace();
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView tvContent;
        public CheckBox cbSelect;

        public ViewHolder(View v) {
            super(v);
            tvContent = (TextView) v.findViewById(R.id.tvContent);
            cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        final ObjectIncome objIncome = myItems.get(position);
        String content = "<b>lalalla</b>";
        holder.tvContent.setText(Html.fromHtml(content));
    }
}

Le problème est, disons que j'ai 10 éléments à l'intérieur du RecyclerView. Lorsque j'ai coché la case à cocher sur l'élément 1, 2, 3, puis je fais défiler vers le bas le RecyclerView, tout à coup certains des autres éléments, par exemple les éléments 8, 9 sont cochés. Et quand je fais défiler à nouveau vers le haut, les éléments 1 et 3 sont cochés mais pas l'élément 2. Une idée de pourquoi cela se produit?


Essayez d'utiliser cette bibliothèque , voir ViewStates. Cela aide à enregistrer un état lors du défilement.
Vitaly

Réponses:


167

C'est un comportement attendu. Vous ne cochez pas votre case ou non. Vous en sélectionnez un et le titulaire de la vue le garde sélectionné. Vous pouvez ajouter une variable booléenne dans votre objet ObjectIncome et conserver le statut de sélection de votre élément.

Vous pouvez regarder mon exemple. Vous pouvez faire quelque chose comme ça:

public class AdapterTrashIncome extends RecyclerView.Adapter<AdapterTrashIncome.ViewHolder> {

    private ArrayList<ObjectIncome> myItems = new ArrayList<>();

    public AdapterTrashIncome(ArrayList<ObjectIncome> getItems, Context context){
        try {
            mContext = context;
            myItems = getItems;
            }catch (Exception e){
            Log.e(FILE_NAME, "51: " + e.toString());
            e.printStackTrace();
        }
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public TextView tvContent;
        public CheckBox cbSelect;

        public ViewHolder(View v) {
            super(v);
            tvContent = (TextView) v.findViewById(R.id.tvContent);
            cbSelect = (CheckBox) v.findViewById(R.id.cbSelect);
        }
    }

    @Override
    public void onBindViewHolder(ViewHolder holder, final int position) {
        final ObjectIncome objIncome = myItems.get(position);
        String content = "<b>lalalla</b>";
        holder.tvContent.setText(Html.fromHtml(content));

        //in some cases, it will prevent unwanted situations
        holder.cbSelect.setOnCheckedChangeListener(null);

        //if true, your checkbox will be selected, else unselected
        holder.cbSelect.setChecked(objIncome.isSelected());

        holder.cbSelect.setOnCheckedChangeListener(new OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                    //set your object's last status
                    objIncome.setSelected(isChecked);
            }
        });

    }
}

19
Ça n'a pas marché. Vous devez écrire holder.cbSelect.setOnCheckedChangeListener(null);avantholder.cbSelect.setChecked(objIncome.isSelected())
Jemshit Iskenderov

2
y a-t-il une raison pour laquelle la définition de holder.cbSelect.setOnCheckedChangeListener (null); travaux?
Déb

4
@oguzhand salut J'ai essayé votre solution mais cela ne fonctionne pas dans les deux cas: avec ou sans réglage de l'auditeur sur null.
Abbas

3
@oguzhand Voici le code de onBindViewHolder. @Override public void onBindViewHolder(final ItemHolder holder, int position) { holder.checkBox.setOnCheckedChangeListener(null); holder.checkBox.setSelected(list.get(position).isSelected()); holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { list.get(holder.getAdapterPosition()).setSelected(isChecked); } });
Abbas

1
@Suisse vous devez conserver le statut de la case à cocher dans un objet, car ViewHolder n'est qu'un titulaire. Si vous avez 100 éléments, vous n'avez qu'environ 6-7 (dépend de la taille de l'écran et de la disposition) ViewHolders et utilisez tous ces objets dans un cycle.
Oğuzhan Döngül

22

En bref, c'est à cause du recyclage des vues et de leur réutilisation!

comment pouvez-vous éviter cela:

1.Cochez onBindViewHoldersi vous devez cocher ou décocher les cases. n'oubliez pas de mettre à la fois if et else

if (...)
    holder.cbSelect.setChecked(true);
else
    holder.cbSelect.setChecked(false);
  1. Mettez un auditeur pour la case à cocher! chaque fois que ses statues vérifiées ont changé, mettez également à jour l'objet correspondant dans votre myItemstableau! Ainsi, chaque fois qu'une nouvelle vue est affichée, elle lit la dernière statue de l'objet.

Votre deuxième point était la clé. Bien que cela fonctionne mieux dans une situation, lorsque l'ensemble de données initial contient également des informations sur l'état vérifié (ce qui est le cas pour moi)
Attila Orosz

1
c'est la réponse la plus directe et la plus correcte. SetCheck pour les DEUX vrai et faux dans onBindViewHolder est la clé
Beeing Jk

Dans mon cas, je dois enregistrer les données dans le modèle de données avec la valeur par défaut isChecked falsepour tous les ensembles de données au démarrage, puis onCheckChangedje viens de mettre à jour la isCheckedvaleur en trueou falseet comme indiqué dans la réponse, implémenter cette vérification cochée ou non.
Ali Tamoor

20

N'UTILISEZ CECI QUE SI VOUS AVEZ UN NOMBRE LIMITÉ D'ARTICLES DANS VOTRE VUE RECYCLEUR.
J'ai essayé d'utiliser la valeur booléenne dans le modèle et de conserver le statut de la case à cocher, mais cela n'a pas aidé dans mon cas. Ce qui a fonctionné pour moi, c'est this.setIsRecyclable (false);

public class ComponentViewHolder extends RecyclerView.ViewHolder {
    public MyViewHolder(View itemView) {
        super(itemView);
        ....
        this.setIsRecyclable(false);
    }

Plus d'explications à ce sujet peuvent être trouvées ici https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable ()

REMARQUE: il s'agit d'une solution de contournement. Pour l'utiliser correctement, vous pouvez vous référer au document qui déclare "Les appels à setIsRecyclable () doivent toujours être appariés (un appel à setIsRecyclabe (false) doit toujours être associé à un appel ultérieur à setIsRecyclable (true)). Les paires d'appels peuvent être imbriquées , car l'état est compté en interne. " Je ne sais pas comment faire cela dans le code, si quelqu'un peut fournir plus de code à ce sujet.


pouvez-vous expliquer comment l'utiliser?
UserName_Untold

44
n'est-ce pas un gaspillage de la logique derrière recyclerView?
Eren

3
J'ai essayé cela avec une longue liste qui a résolu le problème de vérification aléatoire, mais lorsque je fais défiler vers le bas et que je re-fais défiler vers le haut avec une longue liste, les cases à cocher disparaissent :(
SonDang

2
Ce n'est pas une bonne idée de rendre la vue non recyclable car cela épuisera la mémoire et vous perdrez la plupart des avantages de la vue recycleur.
Arthur

Je suis d'accord avec vous les gars, @ eren130 et Arthur. J'ai édité l'article et j'apprécierais beaucoup si nous pouvions trouver un moyen d'utiliser setIsRecyclable (vrai / faux); correctement.
Rana Ranvijay Singh

13

Ajoutez simplement deux méthodes de remplacement de RecyclerView

@Override
public long getItemId(int position) {
    return position;
}

@Override
public int getItemViewType(int position) {
    return position;
}

2
Ne fais pas ça !! Il contournera le mécanisme de recyclage recyclerView et perdra tout l'intérêt de l'utiliser.
Hanoch Moreno

1
Non, ce n'est pas le cas, il renvoie simplement la position exacte de chaque vue recyclée dans le support de vue.
Harish Reddy

1
Harish, peut-être qu'il me manque quelque chose, mais pour autant que je sache, en faisant cela, vous dites en fait à l'adaptateur que le nombre de types d'éléments est le nombre d'éléments. La signification est qu'aucun article ne peut être recyclé car il n'a pas de vue similaire. Il est cependant facile de tester. enregistrez simplement la référence viewHolder.itemView dans onBindViewHolder et voyez s'il y a deux viewHolders détenant la même référence de vue. Le test doit figurer sur une longue liste pour que le système de recyclage soit exécuté.
Hanoch Moreno

3
Il s'est réveillé parfaitement, il a sauvé ma journée.
Kundan

5
au cas où vous auriez plus de 100 articles dans votre vue de recyclage, cette solution chargera tous les articles à la fois, cela pourrait provoquer une exception OutOfMemoryException au cas où vous auriez des images, autrement cette solution est parfaite @Kundan
Harish Reddy

11

Vous pouvez utiliser la classe Model pour suivre la case à cocher de chaque élément recyclerView. La référence complète est de: RecyclerView Checkbox Android

setTag et getTag sont utilisés pour suivre l'état des cases à cocher. Consultez le lien de référence complet pour plus d'informations. Il enseigne également comment envoyer des éléments cochés à NEXTACTIVITY .

Faire un modèle

public class Model {

    private boolean isSelected;
    private String animal;

    public String getAnimal() {
        return animal;
    }

    public void setAnimal(String animal) {
        this.animal = animal;
    }

    public boolean getSelected() {
        return isSelected;
    }

    public void setSelected(boolean selected) {
        isSelected = selected;
    }
}

créer integer.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <integer name="btnplusview">1</integer>
    <integer name="btnpluspos">2</integer>
</resources>

Enfin, l'adaptateur ressemble à ceci:

 import android.content.Context;
 import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
    import android.view.View;
 import android.view.ViewGroup;
 import android.widget.CheckBox;
 import android.widget.TextView;
 import android.widget.Toast;

 import java.util.ArrayList;


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

private LayoutInflater inflater;
public static ArrayList<Model> imageModelArrayList;
private Context ctx;

public CustomAdapter(Context ctx, ArrayList<Model> imageModelArrayList) {

    inflater = LayoutInflater.from(ctx);
    this.imageModelArrayList = imageModelArrayList;
    this.ctx = ctx;
}

@Override
public CustomAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = inflater.inflate(R.layout.rv_item, parent, false);
    MyViewHolder holder = new MyViewHolder(view);

    return holder;
}

@Override
public void onBindViewHolder(final CustomAdapter.MyViewHolder holder, int position) {

    holder.checkBox.setText("Checkbox " + position);
    holder.checkBox.setChecked(imageModelArrayList.get(position).getSelected());
    holder.tvAnimal.setText(imageModelArrayList.get(position).getAnimal());

   // holder.checkBox.setTag(R.integer.btnplusview, convertView);
    holder.checkBox.setTag(position);
    holder.checkBox.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {

            Integer pos = (Integer) holder.checkBox.getTag();
            Toast.makeText(ctx, imageModelArrayList.get(pos).getAnimal() + " clicked!", Toast.LENGTH_SHORT).show();

            if (imageModelArrayList.get(pos).getSelected()) {
                imageModelArrayList.get(pos).setSelected(false);
            } else {
                imageModelArrayList.get(pos).setSelected(true);
            }
        }
    });


}

@Override
public int getItemCount() {
    return imageModelArrayList.size();
}

class MyViewHolder extends RecyclerView.ViewHolder {

    protected CheckBox checkBox;
    private TextView tvAnimal;

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

        checkBox = (CheckBox) itemView.findViewById(R.id.cb);
        tvAnimal = (TextView) itemView.findViewById(R.id.animal);
    }

}

}


3

En utilisant Kotlin, la seule chose qui a résolu ce problème pour moi était d'effacer le OnCheckedChangeListenerparamètre avant de définir la variable, puis de créer un nouveau OnCheckedChangeListeneraprès checked.

Je fais ce qui suit dans mon RecyclerView.ViewHolder

task.setOnCheckedChangeListener(null)
task.isChecked = item.status
task.setOnCheckedChangeListener { _: CompoundButton, checked: Boolean ->
    item.status = checked
    ...
    do more stuff
    ...
}

Cela fonctionne parfaitement. Je ne sais pas pourquoi mais cela ne fonctionne que lorsque quiconque utilise KOTLIN!
Aditya S.

2

Comme indiqué ci-dessus, l'état vérifié de l'objet doit être inclus dans les propriétés de l'objet. Dans certains cas, vous devrez peut-être également modifier l'état de sélection d'objet en cliquant sur l'objet lui-même et laisser la case à cocher informer sur l'état réel (sélectionné ou non). La case à cocher utilisera alors l'état de l'objet à la position réelle de l'adaptateur donné qui est (par défaut / dans la plupart des cas) la position de l'élément dans la liste.

Vérifiez l'extrait ci-dessous, cela peut être utile.

import android.content.Context;
import android.graphics.Bitmap;
import android.net.Uri;
import android.provider.MediaStore;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;

import java.io.File;
import java.io.IOException;
import java.util.List;

public class TakePicImageAdapter extends RecyclerView.Adapter<TakePicImageAdapter.ViewHolder>{
    private Context context;
    private List<Image> imageList;

    public TakePicImageAdapter(Context context, List<Image> imageList) {
        this.context = context;
        this.imageList = imageList;
    }

    @Override
    public TakePicImageAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view= LayoutInflater.from(context).inflate(R.layout.image_item,parent,false);
        return new ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(final TakePicImageAdapter.ViewHolder holder, final int position) {
        File file=new File(imageList.get(position).getPath());
        try {
            Bitmap bitmap= MediaStore.Images.Media.getBitmap(context.getContentResolver(), Uri.fromFile(file));
            holder.image.setImageBitmap(bitmap
            );
        } catch (IOException e) {
            e.printStackTrace();
        }
        holder.selectImage.setOnCheckedChangeListener(null);
        holder.selectImage.setChecked(imageList.get(position).isSelected());
        holder.selectImage.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                holder.selectImage.setChecked(isChecked);
                imageList.get(position).setSelected(isChecked);
            }
        });
        holder.image.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (imageList.get(position).isSelected())
                {
                    imageList.get(position).setSelected(false);
                    holder.selectImage.setChecked(false);
                }else
                {
                    imageList.get(position).setSelected(true);
                    holder.selectImage.setChecked(true);
                }
            }
        });

    }

    @Override
    public int getItemCount() {
        return imageList.size();
    }

    public class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView image;public CheckBox selectImage;
        public ViewHolder(View itemView) {
            super(itemView);
            image=(ImageView)itemView.findViewById(R.id.image);
            selectImage=(CheckBox) itemView.findViewById(R.id.ch);

        }
    }
}


2

Dans mon cas, cela a fonctionné.

@Override
public void onViewRecycled(MyViewHolder holder) {
    holder.checkbox.setChecked(false); // - this line do the trick
    super.onViewRecycled(holder);
}

2

Utilisez un tableau pour conserver l'état des éléments

Dans l'adaptateur, utilisez une carte ou un SparseBooleanArray (qui est similaire à une map mais est une paire clé-valeur de int et boolean) pour stocker l'état de tous les éléments de notre liste d'éléments, puis utilisez les clés et les valeurs pour comparer lors du basculement de l'état vérifié

Dans l'adaptateur, créez un SparseBooleanArray

// sparse boolean array for checking the state of the items

    private SparseBooleanArray itemStateArray= new SparseBooleanArray();

puis dans le gestionnaire de clic d'élément, onClick()utilisez l'état des éléments de l'élément itemStateArray pour vérifier avant de basculer, voici un exemple

        @Override
        public void onClick(View v) {
            int adapterPosition = getAdapterPosition();
            if (!itemStateArray.get(adapterPosition, false)) {
                mCheckedTextView.setChecked(true);
                itemStateArray.put(adapterPosition, true);
            }
            else  {
                mCheckedTextView.setChecked(false);
                itemStateArray.put(adapterPosition, false);
            }
        }

également, utilisez un tableau booléen clairsemé pour définir l'état vérifié lorsque la vue est liée

@Override
public void onBindViewHolder(ViewHolder holder, int position) {
    holder.bind(position);
}

@Override
public int getItemCount() {
    if (items == null) {
        return 0;
    }
    return items.size();
}

 void loadItems(List<Model> tournaments) {
    this.items = tournaments;
    notifyDataSetChanged();
}


class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

    CheckedTextView mCheckedTextView;

    ViewHolder(View itemView) {
        super(itemView);
        mCheckedTextView = (CheckedTextView) itemView.findViewById(R.id.checked_text_view);
        itemView.setOnClickListener(this);
    }

    void bind(int position) {
        // use the sparse boolean array to check
        if (!itemStateArray.get(position, false)) {
            mCheckedTextView.setChecked(false);}
        else {
            mCheckedTextView.setChecked(true);
        }
    }

et l'adaptateur final sera comme ça


1

Vous devez séparer les interactions onBindViewHolder (logic) avec CheckBox et les interactions utilisateur avec checkbox. J'ai utilisé OnCheckedChangeListener pour les interactions utilisateur (évidemment) et ViewHolder.bind () pour la logique, c'est pourquoi vous devez définir l'auditeur vérifié sur null avant de configurer le support et une fois que le support est prêt - configurez l'auditeur vérifié pour les interactions utilisateur.

boolean[] checkedStatus = new boolean[numberOfRows];

@Override
        public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
        final ViewHolderItem itemHolder = (ViewHolderItem) holder;

        //holder.bind should not trigger onCheckedChanged, it should just update UI
        itemHolder.checkBox.setOnCheckedChangeListener(null);

        itemHolder.bind(position);

        itemHolder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                if (isChecked) {
                    checkedStatus[holder.getAdapterPosition()] = true;
                    performCheckedActions(); //your logic here
                } else {
                    checkedStatus[holder.getAdapterPosition()] = false;
                    performUncheckedActions(); //your logic here
                }
            }
        });
    }

public void bind(int position) {
            boolean checked = checkedStatus[position];
            if (checked) {
                checkBox.setChecked(false);
            } else {
                checkBox.setChecked(true);
            }
        }

1

Je recommande de ne pas utiliser checkBox.setOnCheckedChangeListenerdans recyclerViewAdapter. Parce que sur le défilement recyclerView, checkBox.setOnCheckedChangeListenersera déclenché par l'adaptateur. Ce n'est pas sûr . Utilisez plutôt checkBox.setOnClickListenerpour interagir avec les entrées utilisateur.

Par exemple:

     public void onBindViewHolder(final ViewHolder holder, int position) {
        /*
         .
         .
         .
         .
         .
         .
        */

        holder.checkBoxAdapterTasks.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                boolean isChecked =  holder.checkBoxAdapterTasks.isChecked();
                if(isChecked){
                    //checkBox clicked and checked
                }else{
                    //checkBox clicked and unchecked
                }

            }
        });

    }

1

Le problème de cette solution que j'ai trouvé est, en créant un tableau global statique et en l'utilisant dans "onBindViewHolder" ADAPER CLASS, dans lequel j'ai créé toutes les variables / objets globaux nécessaires.

public class RVAdapter extends RecyclerView.Adapter<RVAdapter.PersonViewHolder> {
private Context context;
public static class PersonViewHolder extends RecyclerView.ViewHolder {

    CardView cv;
    TextView question,category;
    TextView personAge;
    ImageView upvote;
    Button b1;
    public static int k;
    private int visibleThreshold = 5;
    public static int i=0;
     static int  check[]; //Static array
    PersonViewHolder(View itemView,int i) {
        super(itemView);
        if(i==PersonViewHolder.k)
        {
            b1=(Button)itemView.findViewById(R.id.loadmore);

        }
        else
        {
            cv = (CardView)itemView.findViewById(R.id.cv);
            question = (TextView)itemView.findViewById(R.id.question);
            category = (TextView)itemView.findViewById(R.id.text_categ);
            personAge = (TextView)itemView.findViewById(R.id.text1);
            upvote = (ImageView)itemView.findViewById(R.id.upvote);

        }

    }

}

Ici (IN CONSTRUCTOR of RVADAPTER CLASS), j'ai donné la taille du tableau égale à la taille / non des éléments que je vais afficher dans la vue du recycleur

List<Person> persons;

RVAdapter(List<Person> persons){
    this.persons = persons;
    PersonViewHolder.check=new int[persons.size()];
    PersonViewHolder.k=persons.size();
}

BindViewHolder, I, Appliqué ce concept sur un bouton, lorsque je clique sur un bouton, l'image de fond du bouton change. L'objet du bouton que j'ai utilisé est des noms comme "upvote", comme "i" tient la position de chaque élément dans la vue du recycleur, je l'ai utilisé comme un index de tableau qui fonctionne comme un drapeau et qui garde la trace de l'état des éléments.

@Override
public void onBindViewHolder(final PersonViewHolder personViewHolder, final int i) {
    if(i==PersonViewHolder.k) {
        personViewHolder.b1.setText("load more");

    }
    else
     {
        personViewHolder.question.setText(persons.get(i).name);
        personViewHolder.personAge.setText(persons.get(i).age);

         if(personViewHolder.check[i]==0)
         {personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);
         }
         else
         {
             personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);

         }

         personViewHolder.upvote.setOnClickListener(new View.OnClickListener() {
             @Override
             public void onClick(View v) {
                 if(personViewHolder.check[i]==0)
                 {personViewHolder.check[i]=1;
                     personViewHolder.upvote.setBackgroundResource(R.drawable.upvote);


                 }
                 else
                 {personViewHolder.check[i]=0;
                     personViewHolder.upvote.setBackgroundResource(R.drawable.noupvote);

                 }


             }
         });
        // personViewHolder.personPhoto.setImageResource(persons.get(i).photoId);
    }

}

1

J'ai eu le même problème. Lorsque je cliquais sur le bouton bascule de l'élément dans mon recyclerView, le bouton bascule coché est apparu dans tous les 10 éléments (par exemple, s'il était cliqué dans l'élément avec un index 0, les éléments avec 9, 18, 27 index étaient également cliqués). Tout d'abord, mon code dans onBindViewHolder était:

if (newsItems.get(position).getBookmark() == 1) {
            holder.getToggleButtonBookmark().setChecked(true);
        }

Mais ensuite j'ai ajouté la déclaration Else

if (newsItems.get(position).getBookmark() == 1) {
            holder.getToggleButtonBookmark().setChecked(true);
//else statement prevents auto toggling
        } else{
            holder.getToggleButtonBookmark().setChecked(false);
        }

Et le problème a été résolu


Merci. Sinon partie effacera la case si elle cochée par défaut lors du recyclage affichera la même vue.
Adarsh ​​Vijayan P

1

ok il y a beaucoup de réponses ici je posterai mon code et je vais simplement expliquer ce que j'ai fait ... ça peut peut-être aider les juniors comme moi:

1- Objectif:

nous allons créer une liste de ce RecyclerViewqui a CheckBoxet RadioButton, quelque chose comme ceci:

entrez la description de l'image ici 2- Classe de modèle

public class ModelClass {
private String time;
private boolean checked;
private boolean free;
private boolean paid;

public TherapistScheduleModel(String time, boolean checked, boolean free, boolean paid) {
    this.time = time;
    this.checked = checked;
    this.free = free;
    this.paid = paid;
}

public boolean isFree() {
    return free;
}

public void setFree(boolean free) {
    this.free = free;
}

public boolean isPaid() {
    return paid;
}

public void setPaid(boolean paid) {
    this.paid = paid;
}

public String getTime() {
    return time;
}

public void setTime(String time) {
    this.time = time;
}

public boolean getChecked() {
    return checked;
}

public void setChecked(boolean checked) {
    this.checked= checked;
}
}

3-Mon incroyable adaptateur

public class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder> {
private Context context;
private ListAllListeners listAllListeners;
private ArrayList<ModelClass> mDataList;

public MyAdapter(Context context, ArrayList<ModelClass> mDataList,
                             ListAllListeners listAllListeners) {
    this.mDataList = mDataList;
    this.listAllListeners = listAllListeners;
    this.context = context;
}

@NonNull
@Override
public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    LayoutInflater inflater = LayoutInflater.from(parent.getContext());
    View view = inflater.inflate(R.layout.single_view, parent, false);
    return new MyViewHolder(view);
}

@Override
public int getItemCount() {
    if (mDataList != null)
        return mDataList.size();
    else
        return 0;
}

@Override
public void onBindViewHolder(@NonNull final MyViewHolder holder, final int position) {
     //important to:
    //setOnCheckedChangeListener to 'null'
    holder.checkBoxTime.setOnCheckedChangeListener(null);
    holder.freeRB.setOnCheckedChangeListener(null);
    holder.paidRB.setOnCheckedChangeListener(null);

    //Check Box
            holder.checkBoxTime.setText(mDataList.get(holder.getAdapterPosition()).getTime());
    //here we check if the item is checked or not from the model.
    if(mDataList.get(holder.getAdapterPosition()).getChecked())
        holder.checkBoxTime.setChecked(true);
    else
        holder.checkBoxTime.setChecked(false);

    holder.checkBoxTime.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            if (b) {
                mDataList.get(holder.getAdapterPosition()).setChecked(true);
                listAllListeners.onItemCheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
            }
            else {
                mDataList.get(holder.getAdapterPosition()).setChecked(false);
                listAllListeners.onItemUncheck(holder.checkBoxTime.getText().toString(), holder.getAdapterPosition());
            }
        }
    });

    //Radio Buttons

    if(mDataList.get(holder.getAdapterPosition()).isFree())
        holder.freeRB.setChecked(true);
    else
        holder.freeRB.setChecked(false);
    holder.freeRB.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
            if (b) {
                mDataList.get(holder.getAdapterPosition()).setFree(true);
                listAllListeners.onFreeCheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
            } else {
                mDataList.get(holder.getAdapterPosition()).setFree(false);
                listAllListeners.onFreeUncheck(holder.freeRB.getText().toString(), holder.getAdapterPosition());
            }
        }
    });

   //***and so on to paidRB***

}//end onBindViewHolder()

public interface ListAllListeners {
//here is a list of clicked listeners to use them as you want ;).
//you can get a list of checked or unChecked of all 
        void onItemCheck(String checkBoxName, int position);
        void onItemUncheck(String checkBoxName, int position);
        void onFreeCheck(String name, int pos);
        void onFreeUncheck(String name, int pos);
        void onPaidCheck(String name, int pos);
        void onPaidUncheck(String name, int pos);
    }

    class MyViewHolder extends RecyclerView.ViewHolder {

        CheckBox checkBoxTime;
        RadioButton freeRB, paidRB;

        MyViewHolder(View itemView) {
            super(itemView);
            checkBoxTime = itemView.findViewById(R.id.timeCheckBox);
            freeRB = itemView.findViewById(R.id.freeRadioBtn);
            paidRB = itemView.findViewById(R.id.paidRadioBtn);
        }
    }//end class MyViewHolder

    }//end class

3- Dans l'activité, vous leur obtenez quelque chose comme ceci:

myAdapter= new MyAdapter(getActivity().getApplicationContext(), mDataList,
                new MyAdapter.ListAllListeners() {

                    @Override
                    public void onItemCheck(String checkBoxName, int position) {
                        Toast.makeText(getActivity(), "" + checkBoxName + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onItemUncheck(String checkBoxName, int position) {
                        Toast.makeText(getActivity(), "" + checkBoxName + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onFreeCheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onFreeUncheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onPaidCheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }

                    @Override
                    public void onPaidUncheck(String name, int position) {

                        Toast.makeText(getActivity(), "" + name + "  " + position, Toast.LENGTH_SHORT).show();
                    }
                });

0

J'ai eu le même problème dans une liste RecyclerView avec des commutateurs, et je l'ai résolu en utilisant la réponse @oguzhand, mais avec ce code dans le checkedChangeListener:

if (buttonView.isPressed()) {
    if (isChecked) {
        group.setSelected(true);
    } else {
        group.setSelected(false);
    }
}else{
    if (isChecked) {
        buttonView.setChecked(false);
    } else {
        buttonView.setChecked(true);
    }
}

(Où 'group' est l'entité que je souhaite sélectionner / désélectionner)


0

classe publique TagYourDiseaseAdapter étend RecyclerView.Adapter {private ReCyclerViewItemClickListener mRecyclerViewItemClickListener; Contexte privé mContext;

List<Datum> deviceList = Collections.emptyList();

/**
 * Initialize the values
 *
 * @param context : context reference
 * @param devices : data
 */

public TagYourDiseaseAdapter(Context context, List<Datum> devices,
                             ReCyclerViewItemClickListener mreCyclerViewItemClickListener) {
    this.mContext = context;
    this.deviceList = devices;
    this.mRecyclerViewItemClickListener = mreCyclerViewItemClickListener;
}


/**
 * @param parent   : parent ViewPgroup
 * @param viewType : viewType
 * @return ViewHolder
 * <p>
 * Inflate the Views
 * Create the each views and Hold for Reuse
 */
@Override
public TagYourDiseaseAdapter.OrderHistoryViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

    View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_tag_disease, parent, false);
    TagYourDiseaseAdapter.OrderHistoryViewHolder myViewHolder = new TagYourDiseaseAdapter.OrderHistoryViewHolder(view);
    return myViewHolder;
}


/**
 * @param holder   :view Holder
 * @param position : position of each Row
 *                 set the values to the views
 */
@Override
public void onBindViewHolder(final TagYourDiseaseAdapter.OrderHistoryViewHolder holder, final int position) {
    Picasso.with(mContext).load(deviceList.get(position).getIconUrl()).into(holder.document);
    holder.name.setText(deviceList.get(position).getDiseaseName());

    holder.radioButton.setOnCheckedChangeListener(null);
    holder.radioButton.setChecked(deviceList.get(position).isChecked());

    //if true, your checkbox will be selected, else unselected
    //holder.radioButton.setChecked(objIncome.isSelected());

    holder.radioButton.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            deviceList.get(position).setChecked(isChecked);
        }
    });


}

@Override
public int getItemCount() {
    return deviceList.size();
}


/**
 * Create The view First Time and hold for reuse
 * View Holder for Create and Hold the view for ReUse the views instead of create again
 * Initialize the views
 */

public class OrderHistoryViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
    ImageView document;
    TextView name;
    CheckBox radioButton;

    public OrderHistoryViewHolder(View itemView) {
        super(itemView);
        document = itemView.findViewById(R.id.img_tag);
        name = itemView.findViewById(R.id.text_tag_name);
        radioButton = itemView.findViewById(R.id.rdBtn_tag_disease);
        radioButton.setOnClickListener(this);
        //this.setIsRecyclable(false);
    }


    @Override
    public void onClick(View view) {
        mRecyclerViewItemClickListener.onItemClickListener(this.getAdapterPosition(), view);
    }
}

}


0

cela se produira lors de l'utilisation setOnCheckedChangeListenerau lieu de cette utilisation setObClickListeneret à l'intérieur de cela, faites simplement cette manipulation facile:

   if (list.get(position).isCheck())
            {
                list.get(position).setCheck(false);
            }
            else
            {
                list.get(position).setCheck(true);
            }

REMARQUE: dans votre modèle de liste, ajoutez une variable booléenne avec le nom checket définissez le getter et le setter pour cela, dans le cas ci-dessus, le mien est setCheck et isCheck

j'espère que cela aidera quelqu'un si oui + votez pour cette réponse



0

cela est dû à la création répétée de la vue, la meilleure option est de vider le cache avant de définir l'adaptateur

recyclerview.setItemViewCacheSize(your array.size());

0

Exemple complet de
classe publique ChildAddressAdapter étend RecyclerView.Adapter <ChildAddressAdapter.CartViewHolder> {

private Activity context;
private List<AddressDetail> addressDetailList;
private int selectedPosition = -1;

public ChildAddressAdapter(Activity context, List<AddressDetail> addressDetailList) {
    this.context = context;
    this.addressDetailList = addressDetailList;
}

@NonNull
@Override
public CartViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {

    LayoutInflater inflater = LayoutInflater.from(context);
    View myView = inflater.inflate(R.layout.address_layout, parent, false);
    return new CartViewHolder(myView);
}

@Override
public void onBindViewHolder(@NonNull CartViewHolder holder, int position) {

    holder.adress_checkbox.setOnClickListener(view -> {
        selectedPosition = holder.getAdapterPosition();
        notifyDataSetChanged();
    });

    if (selectedPosition==position){
        holder.adress_checkbox.setChecked(true);
    }
    else {
        holder.adress_checkbox.setChecked(false);
    }


}

@Override
public int getItemCount() {
    return  addressDetailList.size();
}

class CartViewHolder extends RecyclerView.ViewHolder
{
    TextView address_text,address_tag;
    CheckBox adress_checkbox;

    CartViewHolder(View itemView) {
        super(itemView);
        address_text = itemView.findViewById(R.id.address_text);
        address_tag = itemView.findViewById(R.id.address_tag);
        adress_checkbox = itemView.findViewById(R.id.adress_checkbox);
    }
}

}


-1

Ce qui a fonctionné pour moi, c'est d'annuler les écouteurs sur viewHolder lorsque la vue va être recyclée ( onViewRecycled):

 override fun onViewRecycled(holder: AttendeeViewHolder) {
            super.onViewRecycled(holder)
            holder.itemView.hasArrived.setOnCheckedChangeListener(null);
            holder.itemView.edit.setOnClickListener { null }
        }
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.