CheckBox in RecyclerView keeps on checking different items

Solution 1:

That's an expected behavior. You are not setting your checkbox selected or not. You are selecting one and View holder keeps it selected. You can add a boolean variable into your ObjectIncome object and keep your item's selection status.

You may look at my example. You can do something like that:

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

    }
}

Solution 2:

In short, its because of recycling the views and using them again!

how can you avoid that :

1.In onBindViewHolder check whether you should check or uncheck boxes. don't forget to put both if and else

if (...)
    holder.cbSelect.setChecked(true);
else
    holder.cbSelect.setChecked(false);
  1. Put a listener for check box! whenever its checked statues changed, update the corresponding object too in your myItems array ! so whenever a new view is shown, it read the newest statue of the object.

Solution 3:

Just add two override methods of RecyclerView

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

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

Solution 4:

USE THIS ONLY IF YOU HAVE LIMITED NUMBER OF ITEMS IN YOUR RECYCLER VIEW.
I tried using boolean value in model and keep the checkbox status, but it did not help in my case. What worked for me is this.setIsRecyclable(false);

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

More explanation on this can be found here https://developer.android.com/reference/android/support/v7/widget/RecyclerView.ViewHolder.html#isRecyclable()

NOTE: This is a workaround. To use it properly you can refer the document which states "Calls to setIsRecyclable() should always be paired (one call to setIsRecyclabe(false) should always be matched with a later call to setIsRecyclable(true)). Pairs of calls may be nested, as the state is internally reference-counted." I don't know how to do this in code, if someone can provide more code on this.