You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.19 04:50:40