Checkbox becoming 'sticky' in RecyclerView

2019-08-24 20:56发布

I have a dialog which takes in user input through a list with checkboxes, whose layout is a RecyclerView. But when I select a CheckBox in the list, another CheckBox further down in the list also gets checked, but which I didn't do. These images will help illustrate my point.

Here, I've only selected Calendar and Camera:

enter image description here

but further down in the list, Google and Maps also get selected which I didn't select.

enter image description here

My code for bindActivity is:

public void bindActivity(ResolveInfo resolveInfo)
        {
            mResolveInfo = resolveInfo;
            PackageManager pm = getActivity().getPackageManager();
            String appName = mResolveInfo.loadLabel(pm).toString();
            mAppImageView.setImageDrawable(resolveInfo.loadIcon(pm));
            mAppTextView.setText(appName);
        }

If I add mAppCheckBox.setChecked(false) in bindActivity, then when i go further down in the list and the RecyclerView 'recycles' the list, and then I go up, my earlier selection becomes unselected.

I would love any suggestions on how to get rid of the 'sticky' checkbox.

2条回答
看我几分像从前
2楼-- · 2019-08-24 21:23

You see that behavior, because ViewHolders are being reused. Once you scroll down, the ViewHolder of the upper items (that are not visible now) will be reused for the new items that are going to be displayed. This ViewHolder had a CheckBox that was checked previously and nobody had explicitly said, that it should be unchecked.

You should take care of resetting the state correctly. You can save checked items positions in an array and later in onBindViewHolder() set the selected state of CheckBox appropriately:

public class MyAdapter extends RecyclerView.Adapter<Item> {

  final boolean[] checkedArr;

  public MyAdapter(List<Item> list) {
    checkedArr = new boolean[list.size()];
    // Filling all the items as unchecked by default
    Arrays.fill(checkedArr, false);
  }

  @Override public void onBindViewHolder(Item holder, int position) {
    // You have removed the listener in `onViewRecycled`
    // Thus, this `setChecked()` won't cause any listener to be fired
    holder.checkbox.setChecked(checkedArr[holder.getAdapterPosition()]);
    holder.checkbox.setOnCheckedChangeListener(
        (buttonView, isChecked) -> {
          // Retaining checked state in the array
          checkedArr[holder.getAdapterPosition()] = isChecked;
        });
  }

  @Override public void onViewRecycled(Item holder) {
    super.onViewRecycled(holder);
    // When view is being recycled remove the listener
    holder.checkbox.setOnCheckedChangeListener(null);
  }
  ...
}

Or you can refrain from "reinventing a wheel" and use MultiViewAdapter that is shipping with checkboxes feature: single, single or none, multiple modes.

查看更多
孤傲高冷的网名
3楼-- · 2019-08-24 21:27

a) It doesn't look like you are setting the checked state of the checkbox when you are binding the view. Keep track of the items that have been selected, and then set the checked state when you doing view binding.

b) I'm not exactly sure what you mean, but from what I gather, the dialog documenation might be what you are looking for when communicating with the parent.

查看更多
登录 后发表回答