androidandroid-recyclerview

CheckBox in RecyclerView keeps on checking different items


Here's the XML for my items inside the 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>

And here's the RecyclerView adapter that inflate the layout above for each of its items:

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

The problem is, let's say I have 10 items inside the RecyclerView. When I checked the checkbox on item 1,2,3 then I scroll down the RecyclerView, suddenly some of the other items eg items 8,9 is checked. And when I scroll up again, item 1 and 3 is checked but not item 2. Any idea why this happen?


Solution

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