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()
...
}
}
我的 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()
...
}
}