Search code examples
androidcheckboxpositionandroid-recyclerviewandroid-adapter

How to save check box position in recyclerview?


I have a list of checkListItems. List item has a check box. I want to save the status of the item. Status is saved as 0 or 1.

I am showing the check box checked or unchecked based on status 0 or 1.

It shows correct sometimes but when I scroll up and down more items gets checked or unchecked which I have not done manually.

Adapter:

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

    private List<CheckListItem> checkListItems;
    Context context;
    private String status;

    private final OnItemClickListener listener;

    public interface OnItemClickListener {
        void onItemClick(CheckListItem item);
    }
    public class MyViewHolder extends RecyclerView.ViewHolder {
        public TextView title, budget;
        public RelativeLayout parent;
        private CheckBox checkBox;


        public MyViewHolder(View view) {
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            budget =(TextView) view.findViewById(R.id.budget);
            checkBox = (CheckBox) view.findViewById(R.id.checkBox);

        }


        public void bind(final CheckListItem item, final OnItemClickListener listener) {

            itemView.setOnClickListener(new View.OnClickListener() {
                @Override public void onClick(View v) {
                    listener.onItemClick(item);
                }
            });
        }
    }


    public CheckListItemAdapter(List<CheckListItem> checkListItems,Context context,OnItemClickListener listener) {
        this.checkListItems = checkListItems;
        this.context = context;
        this.listener = listener;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.checklist_item, parent, false);

        return new MyViewHolder(itemView);
    }

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

        final CheckListItem item = checkListItems.get(position);
        holder.title.setText(item.getTitle());
        holder.budget.setText(item.getBudget());

        status = item.getStatus();

        if(status.equals("1"))
        {
            holder.checkBox.setChecked(true);
        }
        if(status.equals("0")) {

            holder.checkBox.setChecked(false);
        }

        holder.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {

                if (isChecked) {

                    status = "1";

                    HashMap<String, String> params = new HashMap<String, String>();


                    params.put("checklistItemId", item.getCheckListItemId());
                    params.put("budget",item.getBudget());
                    params.put("text", item.getTitle());
                    params.put("time_due", item.getDateTime());
                    params.put("reminder",item.getReminder());
                    params.put("status", status);


                    new UpdateCheckListItemsAsyncTask(context).execute(params);


                } else

                {
                    status = "0";

                    HashMap<String, String> params = new HashMap<String, String>();

                    params.put("checklistItemId", item.getCheckListItemId());
                    params.put("budget",item.getBudget());
                    params.put("text", item.getTitle());
                    params.put("time_due", item.getDateTime());
                    params.put("reminder",item.getReminder());
                    params.put("status", status);

                    new UpdateCheckListItemsAsyncTask(context).execute(params);

                }

            }

        });


        holder.bind(checkListItems.get(position), listener);

    }

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

}

What's going wrong here? How can I save the position of checkbox?

Thank you..


Solution

  • I Had faced a similar Issue.

    The thing is, in the RecyclerView, the ViewHolder is Recycled. So, until you remove the OnCheckedChangeListener, the Items will refer to the old OnCheckedChangeListener.

        if(status.equals("1"))
        {
            holder.checkBox.setChecked(true);
        }
        if(status.equals("0")) {
    
            holder.checkBox.setChecked(false);
        }
    

    These lines actually triggers the old onCheckedChangedListener.

    You can try this.

    1. add the line holder.checkBox.setOnCheckedChangeListener(null); as the first Line of onBindViewHolder
    2. You can implement an interface call and shift the onCheckedChanged logic to that interface call using getLayoutPosition()

    As to your second question,

    you can use HashSet to store the data of checked list item. Your way of storing it in class object is also fine

    EDIT- Added Code for reference

        public class CheckListItemAdapter extends RecyclerView.Adapter<CheckListItemAdapter.MyViewHolder> {
        ...
                ..
    
        public CheckListItemAdapter(List<CheckListItem> checkListItems, Context context, OnItemClickListener listener) {
            this.checkListItems = checkListItems;
            this.context = context;
            this.listener = listener;
        }
         @Override
        public void onBindViewHolder(final MyViewHolder holder, int position) {
    
          ....
            ....
    
                if(item.getStatus().equals("1"))
            {
                holder.checkBox.setChecked(true);
            }
            else {
    
                holder.checkBox.setChecked(false);
            }
    
    
    
        public MyViewHolder(View view) {
            super(view);
            title = (TextView) view.findViewById(R.id.title);
            budget = (TextView) view.findViewById(R.id.budget);
            checkBox = (CheckBox) view.findViewById(R.id.checkBox);
            checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                 checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                    @Override
                    public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                        int pos = getLayoutPosition();
                        CheckListItem currentItem=checkListItems.get(pos);
    //to remove redundant calls to network while scrolling
                        if ( isChecked){
                            if (currentItem.getStatus().equals("1")) {
                                return;
                            } else {
                                currentItem.setStatus("1");
                            }
                        } else {
                            if (currentItem.getStatus().equals("0")) {
                                return;
                            } else {
                                currentItem.setStatus("0");
                            }
                        }
    //network call, when check is changed
                        checkChangedLogic(checkListItems.get(pos), isChecked);
                    }
                });
    
        }
    }
    

    Note-

    I have implemented a simple logic in onCheckedChanged to remove redundant calls. Since you mentioned they are network calls, this is necessary.