Android中RecyclerView点击切换背景及默认选中项失效问题
RecyclerView默认选中项导致点击切换背景失效的修复方案
问题描述
在Android应用中使用RecyclerView,需求为:
- 点击条目时切换背景
- 初始默认选中指定项
- 用户点击其他项时,切换选中背景并取消原默认项的选中状态
添加selectedItem = 1设置默认选中项后,点击条目无法切换背景;移除该行代码后功能正常,需解决此问题。
原代码:
class StateAdapter constructor() : RecyclerView.Adapter<StateAdapter.ViewHolder>() { private lateinit var binding: ItemSimpleTxtBinding private lateinit var context: Context private var moviesList = emptyList<StateTypes>() private var selectedItem = -1 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { binding = ItemSimpleTxtBinding.inflate(LayoutInflater.from(parent.context), parent, false) context = parent.context return ViewHolder() } override fun onBindViewHolder(holder: ViewHolder, position: Int) { //getItem from PagingDataAdapter holder.bind(moviesList[position], position) //Not duplicate items holder.setIsRecyclable(false) } override fun getItemCount() = moviesList.size inner class ViewHolder : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") fun bind(item: StateTypes, position: Int) { binding.apply { itemTxt.text = item.name //Click binding.itemCard.setOnClickListener { selectedItem = adapterPosition notifyDataSetChanged() onItemClickListener?.let { it(position) } } //Change color if (selectedItem == adapterPosition) { binding.itemCard.setCardBackgroundColor(ContextCompat.getColor(context, R.color.meatBrown)) } else { binding.itemCard.setCardBackgroundColor(ContextCompat.getColor(context, R.color.white)) } //Default selectedItem = 1 } } } private var onItemClickListener: ((Int) -> Unit)? = null fun setOnItemClickListener(listener: (Int) -> Unit) { onItemClickListener = listener } fun setData(data: List<StateTypes>) { val moviesDiffUtil = NotesDiffUtils(moviesList, data) val diffUtils = DiffUtil.calculateDiff(moviesDiffUtil) moviesList = data diffUtils.dispatchUpdatesTo(this) } class NotesDiffUtils(private val oldItem: List<StateTypes>, private val newItem: List<StateTypes>) : DiffUtil.Callback() { override fun getOldListSize(): Int { return oldItem.size } override fun getNewListSize(): Int { return newItem.size } override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldItem[oldItemPosition] === newItem[newItemPosition] } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldItem[oldItemPosition] === newItem[newItemPosition] } } }
问题原因
你将selectedItem = 1写在了bind方法的末尾,这会导致每次RecyclerView绑定ViewHolder时,selectedItem都会被强制重置为1。也就是说,点击条目后刚把selectedItem改为点击的位置,下一次bind执行时又被改回1,永远无法切换选中状态。
修复方案
1. 正确设置默认选中项
将默认选中的赋值从bind方法中移除,改为在Adapter初始化或数据加载完成后设置(避免数据为空时数组越界)。
2. 优化刷新逻辑
替换notifyDataSetChanged()为局部刷新,只刷新旧选中项和新选中项,提升性能。
修改后的完整代码:
class StateAdapter constructor() : RecyclerView.Adapter<StateAdapter.ViewHolder>() { private lateinit var binding: ItemSimpleTxtBinding private lateinit var context: Context private var moviesList = emptyList<StateTypes>() // 初始化时设置默认选中项,或者在setData中设置更安全 private var selectedItem = 1 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { binding = ItemSimpleTxtBinding.inflate(LayoutInflater.from(parent.context), parent, false) context = parent.context return ViewHolder() } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.bind(moviesList[position], position) holder.setIsRecyclable(false) } override fun getItemCount() = moviesList.size inner class ViewHolder : RecyclerView.ViewHolder(binding.root) { @SuppressLint("SetTextI18n") fun bind(item: StateTypes, position: Int) { binding.apply { itemTxt.text = item.name //Click binding.itemCard.setOnClickListener { val oldSelected = selectedItem selectedItem = adapterPosition // 只刷新旧选中项和新选中项,提升性能 notifyItemChanged(oldSelected) notifyItemChanged(selectedItem) onItemClickListener?.let { it(position) } } //Change color if (selectedItem == adapterPosition) { binding.itemCard.setCardBackgroundColor(ContextCompat.getColor(context, R.color.meatBrown)) } else { binding.itemCard.setCardBackgroundColor(ContextCompat.getColor(context, R.color.white)) } // 移除这里的selectedItem = 1 } } } private var onItemClickListener: ((Int) -> Unit)? = null fun setOnItemClickListener(listener: (Int) -> Unit) { onItemClickListener = listener } fun setData(data: List<StateTypes>) { val moviesDiffUtil = NotesDiffUtils(moviesList, data) val diffUtils = DiffUtil.calculateDiff(moviesDiffUtil) moviesList = data diffUtils.dispatchUpdatesTo(this) // 如果需要确保数据加载后再设置默认选中(比如数据可能为空),可以在这里设置 // if (moviesList.size > 1) selectedItem = 1 } class NotesDiffUtils(private val oldItem: List<StateTypes>, private val newItem: List<StateTypes>) : DiffUtil.Callback() { override fun getOldListSize(): Int { return oldItem.size } override fun getNewListSize(): Int { return newItem.size } override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldItem[oldItemPosition] === newItem[newItemPosition] } override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean { return oldItem[oldItemPosition] === newItem[newItemPosition] } } }
额外说明
- 如果你的数据是异步加载的,建议在
setData方法中判断数据长度后再设置selectedItem = 1,避免数组越界。 - 若无特殊需求,建议移除
holder.setIsRecyclable(false),RecyclerView的复用机制能提升性能,强制关闭复用可能导致内存问题。
内容的提问来源于stack exchange,提问作者Dr.KeyOk
相关产品推荐
相关产品推荐

