RecyclerView 和 notifyDataSetChanged LongClick 不匹配
Posted
技术标签:
【中文标题】RecyclerView 和 notifyDataSetChanged LongClick 不匹配【英文标题】:RecyclerView and notifyDataSetChanged LongClick mismatch 【发布时间】:2021-12-09 21:32:15 【问题描述】:我的 Recycler Adapter 中的 notifyDataSetChanged() 有一个奇怪的问题。如果我在一个数组中保留 5 个项目,则代码可以正常工作,我可以选中我 LongClick 项目上的复选框,但是当我向数组中添加 5 个或更多项目时,其他复选框会在我的列表中被选中。
当用户 LongClicks 时,我使用布尔值在复选框上的 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
我应该补充一点,当我移除复选框的切换开关并在第一次加载时显示复选框时,点击匹配复选标记没有问题。
有人知道发生了什么吗?我们将不胜感激所有帮助!
【问题讨论】:
如果您需要更多代码,我很乐意添加一些! 【参考方案1】:问题是您在 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()
...
【讨论】:
太棒了!虽然我不同意你关于 notifyItemChanged(position) 的想法。在那种情况下,我可能更喜欢 notifyItemRangeChanged(0, items.size) ,但我想这是一个品味问题 ;-) 我在这个问题上徘徊,但我也(在我的帖子之外)向我的 SQLite 数据库添加了一个 INTEGER 属性以检查已检查的项目。这样干净多了。notifyItemRangeChanged(0, items.size)
比notifyDataSetChanged
好,但它仍然告诉适配器每个项目都需要更新。 notifyItemChanged(position)
字面上只是“嘿,这个项目已经改变了,所以更新它”这就是正在发生的事情,它更有效,但它也更能描述你在做什么。长按项目->更新该项目的状态->触发该项目的UI刷新以反映新状态
如果你想在应用程序运行之间保持这种状态,你可能还需要那个 SQL 列,但是是的,你需要单独存储和更新该状态 - 读取和写入数据库太慢了对于 UI 更改,因此您也需要该内存版本。此外,如果您想提高效率,您可能希望在每个视图持有者 once 上设置点击侦听器(通常在视图持有者的 init
块中进行)并给它一个 position
财产。然后你将它的position
设置为onBindViewHolder
(它当前显示的位置),点击监听器只是参考那个以上是关于RecyclerView 和 notifyDataSetChanged LongClick 不匹配的主要内容,如果未能解决你的问题,请参考以下文章
BaseQuickAdapter和BaseViewHolder使用 RecyclerView点击事件 RecyclerView添加子view点击事件 RecyclerView添加子view点击事