tux-world
tux-world

Reputation: 2720

Android change CheckBoxes on Adapter dont work correctly

In my simple adapater, I want to manage checkboxes on items. When I click on a check box I change a status and when I'm scrolling the checkbox status is false and i can't fix my code to resolve this problem

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.CustomContactsViewHolder> {
    private OnCardClickListener onCardClickListener;
    private List<UserPhoneContacts> list = Collections.emptyList();
    private       Context            context;
    private       Realm              realm;
    public static OnSelectedContacts onSelectedContacts;
    private Map<String, Boolean> checkBoxStates = new HashMap<>();

    public ContactsAdapter(List<UserPhoneContacts> list, Context context) {
        this.list = list;
        this.context = context;
        this.realm = Realm.getDefaultInstance();
    }

    @Override
    public CustomContactsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View                     v      = LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_item, parent, false);
        CustomContactsViewHolder holder = new CustomContactsViewHolder(v);
        return holder;
    }

    @Override
    public void onBindViewHolder(final CustomContactsViewHolder holder, final int position) {
        holder.contact_name.setText(list.get(position).getDisplayName());
        holder.contact_mobile.setText(list.get(position).getMobileNumber());

        Boolean checkedState = checkBoxStates.get(list.get(position).getMobileNumber());
        holder.select_contact.setChecked(checkedState == null ? false : checkedState);
        holder.select_contact.setTag(position);
        holder.select_contact.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                onChange((Integer) v.getTag());
            }
        });
    }

    private void onChange(int position) {
        final UserPhoneContacts item = list.get(position);
        if (item == null) {
            return;
        }
        boolean checkedState = checkBoxStates.get(list.get(position).getMobileNumber()) != null;
        checkBoxStates.put(item.getMobileNumber(), !checkedState);
        notifyDataSetChanged();
    }

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

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    public void setData(List<UserPhoneContacts> list) {
        this.list = list;
    }

    public static class CustomContactsViewHolder extends RecyclerView.ViewHolder {
        @BindView(R.id.contact_name)
        TextView contact_name;

        @BindView(R.id.contact_mobile)
        TextView contact_mobile;

        @BindView(R.id.contact_photo)
        CircleImageView contact_photo;

        @BindView(R.id.select_contact)
        CheckBox select_contact;

        CustomContactsViewHolder(View view) {
            super(view);
            ButterKnife.bind(this, view);
        }
    }
}

what happen when i clicking on checkbox?

click->checked, click->unchecked, click-> unchecked, click-> unchecked, click-> unchecked

after unchecked checkbox i can't change checkbox state after that by click on that

Upvotes: 3

Views: 2370

Answers (3)

Ready Android
Ready Android

Reputation: 3632

Here I made your CustomContactsViewHolder class as non static class, and did some changes regarding click events and setting data again to check and uncheck, once check with full code by taking your code as a backup.

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 java.util.Collections;
import java.util.HashMap;
import java.util.Map;

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.CustomContactsViewHolder> {
    private OnCardClickListener onCardClickListener;
    private List<UserPhoneContacts> list = Collections.emptyList();
    private Context context;
    private       Realm              realm;
    public static OnSelectedContacts onSelectedContacts;
    private Map<String, Boolean> checkBoxStates = new HashMap<>();

    public ContactsAdapter(List<UserPhoneContacts> list, Context context) {
        this.list = list;
        this.context = context;
        this.realm = Realm.getDefaultInstance();
    }

    @Override
    public CustomContactsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View v      = LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_item, parent, false);
        CustomContactsViewHolder holder = new CustomContactsViewHolder(v);
        return holder;
    }

    @Override
    public void onBindViewHolder(final CustomContactsViewHolder holder, final int position) {
        holder.contact_name.setText(list.get(position).getDisplayName());
        holder.contact_mobile.setText(list.get(position).getMobileNumber());
        boolean checkedState = checkBoxStates.containsKey(list.get(position).getMobileNumber())?checkBoxStates.get(list.get(position).getMobileNumber()):false;
        holder.select_contact.setChecked(checkedState);
    }

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

    @Override
    public void onAttachedToRecyclerView(RecyclerView recyclerView) {
        super.onAttachedToRecyclerView(recyclerView);
    }

    public void setData(List<UserPhoneContacts> list) {
        this.list = list;
    }

    public class CustomContactsViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        @BindView(R.id.contact_name)
        TextView contact_name;

        @BindView(R.id.contact_mobile)
        TextView contact_mobile;

        @BindView(R.id.contact_photo)
        CircleImageView contact_photo;

        @BindView(R.id.select_contact)
        CheckBox select_contact;


        CustomContactsViewHolder(View view) {
            super(view);
            ButterKnife.bind(this, view);
        }

        @OnClick(R.id.select_contact)
        @Override
        public void onClick(View v) {
            int position = getAdapterPosition();
            switch (v.getId()){
                case R.id.select_contact:
                    final UserPhoneContacts item = list.get(position);
                    if (item == null) {
                        return;
                    }
                    boolean checkedState = checkBoxStates.containsKey(list.get(position).getMobileNumber())?checkBoxStates.get(list.get(position).getMobileNumber()):false;
                    ((CheckBox)v).setChecked(!checkedState);
                    checkBoxStates.put(item.getMobileNumber(), !checkedState);
                    ContactsAdapter.this.notifyDataSetChanged();
                    break;
            }
        }
    }
}

Upvotes: 1

Johnny Shieh
Johnny Shieh

Reputation: 86

The leading cause of your problem is onChange method.

private void onChange(int position) {
    final UserPhoneContacts item = list.get(position);
    if (item == null) {
        return;
    }
    boolean checkedState = checkBoxStates.get(list.get(position).getMobileNumber()) != null;
    checkBoxStates.put(item.getMobileNumber(), !checkedState);
    notifyDataSetChanged();
}

For example, firstly you click position 3 item, checkBoxStates.get(3) return null, so !checkedState will be true.

Secondly you click position 3, checkBoxStates.get(3) == True, checkedState = (True != null) = true, so !checkedState will false.

Thirdly you click position 3, checkBoxStates.get(3) == False, checkedState = (False != null) = true, so !checkedState also will false, the new state will also be unchecked.

The fixed code should be as following:

private void onChange(int position) {
    final UserPhoneContacts item = list.get(position);
    if (item == null) {
        return;
    }
    Boolean lastCheckedState = checkBoxStates.get(list.get(position).getMobileNumber());
    boolean checkedState = (null == lastCheckedState) ? false : lastCheckedState;
    checkBoxStates.put(item.getMobileNumber(), !checkedState);
    notifyDataSetChanged();
}

Upvotes: 0

Shashanth
Shashanth

Reputation: 5190

You don't need to create a separate Map<String, Boolean> list to maintain whether your CheckBox is selected or not. Try the simple trick below. But you need to update your UserPhoneContacts model class as follows.

public class UserPhoneContacts {

  // other variables

  private boolean isSelected = false;    // add this

  // your class constructor and other getter/setter methods

  // add below getter method
  public boolean isSelected() {
    return this.isSelected;
  }

  // add below setter method
  public void setSelected(boolean isSelected) {
    this.isSelected = isSelected;
  }
}

Change your ContactAdapter class like this

@Override
public void onBindViewHolder(final CustomContactsViewHolder holder, final int position) {
    ...
    final UserPhoneContacts upc = list.get(position);
    holder.select_contact.setChecked(upc.isSelected() ? true : false);

    holder.select_contact.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View view) {
            upc.setSelected(!upc.isSelected());
            holder.view.setChecked(upc.isSelected() ? true : false);
        }
    });
}

Upvotes: 1

Related Questions