安卓上适配器中的复选框更改不能正常工作

3
在我的简单适配器中,我想管理项目上的复选框。当我点击复选框时,我会改变其状态,但当我滚动时,复选框状态会变为false,我无法解决这个问题。
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);
        }
    }
}

当我点击复选框时会发生什么?

点击->选中,点击->取消选中,点击->取消选中,点击->取消选中,点击->取消选中

在取消选中复选框后,我无法通过再次点击更改复选框状态。


onChange() 方法中,在 checkBoxStates.put(item.getMobileNumber(), checked); 这一行之后添加 notifyDatasetChanged(); - Anil Ravsaheb Ghodake
@Anil 我遇到了这个错误:java.lang.IllegalStateException: Cannot call this method while RecyclerView is computing a layout or scrolling - tux-world
每次滚动时,都会调用setOnCheckedChangeListener,请尝试使用readyandroid的答案进行检查。 - Ready Android
你把这行代码[holder.select_contact.setChecked(checkedState == null ? false : true);]从readyandroid的答案中复制错了,请检查答案并更新。根据你的代码,在滚动时,如果mobilenumber的值不为空,则每次都会将checkbox设置为true。 - Ready Android
使用视图标签和哈希映射来维护列表中每个复选框的选中状态。 - Amrut Bidri
@AmrutBidri,你能帮我修复一下我的代码吗? - tux-world
3个回答

1

我将CustomContactsViewHolder类改为非静态类,并对点击事件和数据设置进行了一些更改,以便检查和取消检查,一旦备份您的代码,再使用完整代码进行检查。

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

当我再次单击已选中的复选框并且状态在再次选中后将取消选中时,状态不会改变。 - tux-world
LogCat: E/checkedState: false E/checkBoxStates: {09122331743=true} E/checkedState: true E/checkBoxStates: {09122331743=false} E/checkedState: true E/checkBoxStates: {09122331743=false} 日志位置是 boolean checkedState = checkBoxStates.get(list.get(position).getMobileNumber()) == null ? false : true; Log.e("checkedState ", checkedState + ""); checkBoxStates.put(item.getMobileNumber(), !checkedState); Log.e("checkBoxStates ", checkBoxStates.toString()); notifyDataSetChanged(); - tux-world
我已经根据你的帖子更新了我的代码,但是对于再次选中和取消选中,它并不能正常工作。现在滚动列表方面我没有任何问题。 - tux-world
我只是在这行代码中将 boolean 改为了 BooleanBoolean checkedState = checkBoxStates.get(list.get(position).getMobileNumber()); - tux-world
你有没有查看我在你的问题帖子中最新的评论,请检查一下并让我知道? - Ready Android
显示剩余3条评论

1
你不需要创建一个单独的Map<String, Boolean>列表来维护你的CheckBox是否被选中。尝试下面的简单技巧。但你需要按照以下方式更新你的UserPhoneContacts模型类。
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;
  }
}

将您的ContactAdapter类更改为以下内容。
@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);
        }
    });
}

我必须创建Map,因为我正在使用Realm数据库解决方案,并且需要更改数据库上的数据。 - tux-world
好的,你可以使用<Map>。使用上述逻辑来检查CheckBox是否被选中。根据模型类返回的布尔值,您可以更新Map条目,对吧? - Shashanth
是的,我的代码逻辑是正确的,但当我尝试多次点击更改复选框状态时,它无法正常工作。我的帖子已更新。 - tux-world
使用 onChange(position); 而不是 onChange((Integer) v.getTag());,这可能是问题所在。 - Shashanth
不,我的复选框状态在更改一次后不会在“onChange”函数中改变。 - tux-world

0
你的问题主要原因是 onChange 方法。
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();
}

例如,首先你点击位置 3 的项目,checkBoxStates.get(3) return null,所以!checkedState 将会为 true。
其次,你再次点击位置 3,checkBoxStates.get(3) == TruecheckedState = (True != null) = true,所以!checkedState 将会为 false。
第三个例子,你再次点击位置 3,checkBoxStates.get(3) == FalsecheckedState = (False != null) = true,所以!checkedState 也将会为 false,新的状态也将是未选中。
修复后的代码应该如下所示:
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();
}

我测试了你的代码,但它不能正常工作。在点击复选框后,状态从未改变。 - tux-world

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接