如何在RecyclerView中点击切换Item背景色且不使用notifyDataSetChanged
最佳解决方案:精准刷新选中/取消选中的Item
你当前用notifyDataSetChanged()会强制刷新整个列表,完全浪费了RecyclerView的局部刷新能力。正确的做法是只刷新之前选中的Item和新选中的Item,具体修改步骤如下:
1. 修改点击事件的刷新逻辑
在ViewHolder的点击监听里,先记录旧的选中位置,更新新的选中位置后,分别对两个位置调用notifyItemChanged():
binding.root.setOnClickListener { val oldSelected = selectedItem selectedItem = adapterPosition // 刷新旧选中项(如果存在) if (oldSelected != -1) { notifyItemChanged(oldSelected) } // 刷新新选中项 notifyItemChanged(selectedItem) onItemClickListener?.invoke(item) }
2. 移除不必要的setIsRecyclable(false)
这行代码会禁用RecyclerView的View复用机制,严重影响性能,直接删除holder.setIsRecyclable(false)即可——只要你的bind()方法能正确根据selectedItem状态设置UI,复用完全没问题。
3. 优化DiffUtils的判断逻辑(可选但推荐)
你当前的NotesDiffUtils里用===引用判断,对于数据类来说,应该用内容相等判断更合理,比如如果Data是数据类,可以改成:
override fun areItemsTheSame(oldPos: Int, newPos: Int): Boolean { return oldItem[oldPos].id == newItem[newPos].id // 用唯一标识判断是否是同一个Item } override fun areContentsTheSame(oldPos: Int, newPos: Int): Boolean { return oldItem[oldPos] == newItem[newPos] // 数据类自动生成equals,判断内容是否一致 }
修改后的核心代码片段
inner class ViewHolder : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") fun bind(item: Data) { binding.apply { selectedTxt.text = item.description // 点击事件处理 binding.root.setOnClickListener { val oldSelected = selectedItem selectedItem = adapterPosition if (oldSelected != -1) { notifyItemChanged(oldSelected) } notifyItemChanged(selectedItem) onItemClickListener?.invoke(item) } // 根据选中状态设置UI val isSelected = selectedItem == adapterPosition binding.selectedImg.setImageResource( if (isSelected) R.drawable.bg_circle_selected else R.drawable.bg_circle_un_selected ) } } } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(moviesList[position]) // 移除禁用复用的代码 }
方案优势
- 性能更优:最多仅刷新2个Item,避免全量刷新带来的性能损耗
- 符合设计理念:充分利用RecyclerView的局部刷新机制,保留View复用特性
- 逻辑清晰:明确控制需要更新的位置,代码可读性更强
内容的提问来源于stack exchange,提问作者Jojo Drink
相关产品推荐
相关产品推荐

