RecyclerView 和 notifyDataSetChanged LongClick 不匹配

RecyclerView and notifyDataSetChanged LongClick mismatch

我的 Recycler 适配器中的 notifyDataSetChanged() 有一个奇怪的问题。如果我在一个数组中保留 5 个项目,代码工作正常,我可以选中我 LongClick 项目的复选框,但是当我向数组添加 5 个或更多项目时,其他复选框会在我的列表中被选中。

当用户长点击时,我也在使用布尔值在复选框上的 VISIBLE 和 GONE 之间切换。

这是我的代码:

class RecyclerAdapter(private val listActivity: ListActivity) : RecyclerView.Adapter<RecyclerAdapter.Holder>() {

    lateinit var binding: ActivityListItemRowBinding
    var checkboxesVisibility = false
    val dummyArrayWorks = arrayOf("000", "111", "222", "333", "444")
    val dummyArrayFails = arrayOf("000", "111", "222", "333", "444", "555")

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
        binding = ActivityListItemRowBinding.inflate(LayoutInflater.from(parent.context), parent, false)
        return Holder(binding)
    }

    override fun getItemCount(): Int = dummyArrayFails.size

    @SuppressLint("NotifyDataSetChanged")
    override fun onBindViewHolder(holder: Holder, position: Int) {

        val item = dummyArrayFails[position]
        
        holder.binding.checkbox.visibility = if (checkboxesVisibility) VISIBLE else GONE
        holder.bindItem(item)

        holder.itemView.setOnLongClickListener {
            if (!checkboxesVisibility) {
                checkboxesVisibility = true
                holder.binding.checkbox.isChecked = true
                notifyDataSetChanged()
                true
            } else {
                false
            }
        }
        holder.itemView.setOnClickListener {
            if (!checkboxesVisibility) {
                //Some other unrelated code
            } else {
                holder.binding.checkbox.isChecked = !holder.binding.checkbox.isChecked
                notifyDataSetChanged()
            }
        }
    }

    class Holder(internal val binding: ActivityListItemRowBinding) : RecyclerView.ViewHolder(binding.root) {

        var item = String()

        fun bindItem(item: String) {
            this.item = item
            binding.itemPlaceHolder.text = item
        }
    }
}

我应该补充一点,当我移除复选框的切换开关,并在第一次加载时只显示复选框时,点击与复选标记匹配没有问题。

有人知道发生了什么事吗?非常感谢所有帮助!

问题是您在 ViewHolder 本身中保持选中状态 - 您根据点击打开和关闭其复选框,对吗?

RecyclerView 的工作方式是,它不会为每个项目都创建一个 ViewHolder(就像 ListView 那样),它只会创建其中的一小部分 - 足以屏幕上的内容以及一些用于滚动的内容 - 回收这些内容,使用它们来显示不同的项目。

这就是 onBindViewHolder 的意义所在 - 当它需要在 position 显示项目时,它会从它的池中给你一个 ViewHolder 并说给你,用它来显示此项目的详细信息。在这里您可以执行设置文本、更改图像以及设置复选框状态等操作以反映特定项目。


您所做的是您没有在任何地方存储项目的状态,您只是在 view holder 上设置复选框。因此,如果您选中它,恰好显示在该可重复使用的持有人对象中的每个项目都将勾选其框。这就是为什么您看到它在其他项目上弹出的原因 - 选中状态与项目本身无关,只是因为它们在列表中的位置而恰好使用了哪个视图持有者。

因此,您需要将它们的选中状态保存在某处 - 它可以像匹配项目列表长度的布尔数组一样简单。然后你只需在绑定数据(显示它)时设置并从中获取。使用你所拥有的:

// all default to false
val itemChecked = BooleanArray(items.size)

override fun onBindViewHolder(holder: Holder, position: Int) {
    ...
    // when displaying the data, refer to the checked state we're holding
    holder.binding.checkbox.checked = itemChecked[position]
    ...
    holder.itemView.setOnLongClickListener {
        ...
        // when checking the box, update our checked state
        // since we're calling notifyDataSetChanged, the item will be redisplayed
        // and onBindViewHolder will be called again (which sets the checkbox)
        itemChecked[position] = true
        // notifyItemChanged(position) is better here btw, just refreshes this one
        notifyDataSetChanged()
        ...
    }
}