RecyclerView选中/未选中项颜色切换功能完善求助
RecyclerView单选颜色切换功能完善方案
我需要实现RecyclerView的单选颜色切换功能:点击某一项时,该项切换为选中颜色;点击另一项时,原选中项恢复默认颜色,新点击项变为选中颜色。目前已实现点击选中项可恢复默认颜色的功能,但缺少点击新的未选中项时的颜色切换逻辑。
我的代码
SubItem类
class SubItem(val channel: Channel) : Item<GroupieViewHolder>() { @SuppressLint("ResourceAsColor") override fun bind(viewHolder: GroupieViewHolder, position: Int) { val profileImageUrl = channel.channel_logo viewHolder.itemView.sub_item_name.text = channel.channel_name viewHolder.itemView.sub_item_layout.setBackgroundResource(R.color.white) viewHolder.itemView.sub_item_name.setTextColor(R.color.colorSecondaryText) val targetImageView = viewHolder.itemView.sub_item_profile try { Picasso.get().load(profileImageUrl) .placeholder(R.drawable.ic_baseline_account_circle_24) .into(targetImageView) }catch (e:Exception){ Log.d("SubItem","${e.message}") } viewHolder.itemView.sub_item_layout.setOnClickListener { if (selected_position == position){ selected_position = null viewHolder.itemView.sub_item_layout.setBackgroundResource(R.color.white) viewHolder.itemView.sub_item_name.setTextColor(R.color.colorSecondaryText) } else{ selected_position = position viewHolder.itemView.sub_item_layout.setBackgroundResource(R.color.colorSecondaryText) viewHolder.itemView.sub_item_name.setTextColor(R.color.black) } } } override fun getLayout(): Int { return R.layout.subscription_item } }
列表加载函数fetchSubs
private fun fetchSubs() { val uid = auth.uid val user = database.getReference("/users/$uid/subscriptions") val adapter = GroupAdapter<GroupieViewHolder>() user.addListenerForSingleValueEvent(object : ValueEventListener{ @SuppressLint("NotifyDataSetChanged") override fun onDataChange(p0: DataSnapshot) { p0.children.forEach{ val sub = it.getValue(Subscription::class.java) ?: return if (sub.subscribed == true) { val ref = database.getReference("/channels/${sub.channel_uid}") ref.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(p0: DataSnapshot) { val channel = p0.getValue(Channel::class.java) ?: return adapter.add(SubItem(channel)) } override fun onCancelled(error: DatabaseError) { } }) } } adapter.setOnItemClickListener{ item, view -> val subItem = item as SubItem val channelName = subItem.channel.channel_name val channelUid = subItem.channel.uid Toast.makeText(requireContext(),"$channelName : $channelUid", Toast.LENGTH_SHORT).show() fetchSubs() } sub_recyclerview.adapter = adapter } override fun onCancelled(error: DatabaseError) { } }) }
问题分析
你的代码核心问题在于:selected_position如果是SubItem类的成员变量,会导致每个列表项独立维护选中状态,无法全局同步;同时点击后重复调用fetchSubs()会重新加载整个列表,既浪费性能又丢失当前选中状态。
修复方案
1. 定义全局选中位置变量
在你的Fragment/Activity中添加全局变量统一管理选中状态:
private var selectedPosition: Int? = null
2. 修改SubItem类,根据选中状态渲染UI
将选中状态作为参数传入SubItem,并在bind方法中根据状态设置样式:
class SubItem(val channel: Channel, val isSelected: Boolean) : Item<GroupieViewHolder>() { @SuppressLint("ResourceAsColor") override fun bind(viewHolder: GroupieViewHolder, position: Int) { val profileImageUrl = channel.channel_logo viewHolder.itemView.sub_item_name.text = channel.channel_name // 根据选中状态设置样式 if (isSelected) { viewHolder.itemView.sub_item_layout.setBackgroundResource(R.color.colorSecondaryText) viewHolder.itemView.sub_item_name.setTextColor(R.color.black) } else { viewHolder.itemView.sub_item_layout.setBackgroundResource(R.color.white) viewHolder.itemView.sub_item_name.setTextColor(R.color.colorSecondaryText) } val targetImageView = viewHolder.itemView.sub_item_profile try { Picasso.get().load(profileImageUrl) .placeholder(R.drawable.ic_baseline_account_circle_24) .into(targetImageView) } catch (e: Exception) { Log.d("SubItem", "${e.message}") } } override fun getLayout(): Int { return R.layout.subscription_item } }
3. 重构fetchSubs函数,正确管理选中状态
移除重复加载逻辑,点击时仅更新选中状态并刷新列表:
private fun fetchSubs() { val uid = auth.uid val user = database.getReference("/users/$uid/subscriptions") val adapter = GroupAdapter<GroupieViewHolder>() val channelList = mutableListOf<Channel>() user.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(p0: DataSnapshot) { p0.children.forEach { val sub = it.getValue(Subscription::class.java) ?: return@forEach if (sub.subscribed == true) { val ref = database.getReference("/channels/${sub.channel_uid}") ref.addListenerForSingleValueEvent(object : ValueEventListener { override fun onDataChange(p0: DataSnapshot) { val channel = p0.getValue(Channel::class.java) ?: return@onDataChange channelList.add(channel) // 更新Adapter,传入每个项的选中状态 adapter.update(channelList.mapIndexed { index, channel -> SubItem(channel, index == selectedPosition) }) } override fun onCancelled(error: DatabaseError) {} }) } } adapter.setOnItemClickListener { item, view -> val clickedPosition = adapter.getPosition(item) val subItem = item as SubItem val channelName = subItem.channel.channel_name val channelUid = subItem.channel.uid Toast.makeText(requireContext(), "$channelName : $channelUid", Toast.LENGTH_SHORT).show() // 更新选中位置:点击当前选中项则取消,否则选中新项 selectedPosition = if (selectedPosition == clickedPosition) { null } else { clickedPosition } // 刷新列表,重新渲染所有项的选中状态 adapter.update(channelList.mapIndexed { index, channel -> SubItem(channel, index == selectedPosition) }) } sub_recyclerview.adapter = adapter } override fun onCancelled(error: DatabaseError) {} }) }
关键说明
- 全局维护
selectedPosition,确保所有列表项共享同一选中状态 - 在
bind方法中根据isSelected参数动态设置样式,保证刷新时状态正确 - 点击事件仅更新选中状态并刷新列表,避免重复加载数据
- 使用
adapter.update()替换重复添加项,保证列表数据与选中状态同步
内容的提问来源于stack exchange,提问作者Andrey
相关产品推荐
相关产品推荐

