RecyclerView搭配FloatingActionButton选中状态不生效问题排查
问题:RecyclerView(横向GridLayoutManager)选中状态UI不生效
我正在开发一个使用GridLayoutManager的RecyclerView,布局代码如下:
val layoutManager = GridLayoutManager(requireActivity(), 2, GridLayoutManager.HORIZONTAL, false)
目前遇到的问题是:除第一行第三个条目外,其他条目选中时无法改变颜色和标记选中状态,但在setOnClickListener中打印日志能获取到正确的Item名称和ID,仅UI层面的选中标记不生效。
适配器代码
package com.example.ui.home.collection.adapter import android.content.Context import android.content.res.ColorStateList import android.graphics.Color import android.util.Log import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.example.abc.R import com.example.abc.data.network.responses.success.CollectionCustomersResponse import com.example.abc.databinding.ItemCollectionTypeBinding import com.example.abc.ui.home.collection.adapter.viewHolder.ItemViewHolder class ItemAdapter( private val items: List<CollectionCustomersResponse.Items>, private val context: Context, private var onItemClicked: ((item: CollectionCustomersResponse.Items) -> Unit) ) : RecyclerView.Adapter<ItemViewHolder>() { private lateinit var binding: ItemCollectionTypeBinding private var selectedItemID :Long = 0 override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ItemViewHolder { binding = ItemCollectionTypeBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ItemViewHolder(binding) } override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { val item = items[position] val selectedColorState = ColorStateList.valueOf(context.resources.getColor(R.color.main_color)) val unSelectedColorState = ColorStateList.valueOf(context.resources.getColor(R.color.grey_shade)) binding.fab.setOnClickListener { setSelectedItemID(item.CustomerInventoryID) onItemClicked(item) } if (selectedItemID == item.CustomerInventoryID) { binding.fab.setImageResource(R.drawable.item_selected1) binding.fab.backgroundTintList = selectedColorState } else { binding.fab.setImageResource(R.drawable.item_unselected) binding.fab.backgroundTintList = unSelectedColorState } binding.tvItemName.setTextIsSelectable(true) holder.bind(item) } private fun setSelectedItemID (itemID: Long) { this.selectedItemID = itemID notifyDataSetChanged() } override fun getItemCount(): Int = items.size }
条目XML代码
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools"> <data> <variable name="item" type="com.example.abc.data.response.CollectionCustomersResponse.Items" /> </data> <androidx.constraintlayout.widget.ConstraintLayout android:id="@+id/container" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="4sp"> <com.google.android.material.floatingactionbutton.FloatingActionButton android:id="@+id/fab" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="4dp" android:elevation="5dp" android:src="@drawable/item_unselected" android:background="?android:attr/selectableItemBackground" app:backgroundTint="@color/grey_shade" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.482" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" tools:visibility="visible" /> <TextView android:id="@+id/tvItemName" android:layout_width="77sp" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginStart="4dp" android:layout_marginTop="10dp" android:layout_marginEnd="4dp" android:fontFamily="@font/inter_medium" android:gravity="center_horizontal" android:ellipsize="marquee" android:fadingEdge="horizontal" android:marqueeRepeatLimit="marquee_forever" android:scrollHorizontally="true" android:singleLine="true" android:text="@{item.itemName}" android:textColor="@color/black" android:textSize="14sp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="@+id/fab" app:layout_constraintStart_toStartOf="@+id/fab" app:layout_constraintTop_toBottomOf="@id/fab" tools:text="Bed Sheets" /> </androidx.constraintlayout.widget.ConstraintLayout> </layout>
问题原因
核心问题是适配器共享了同一个全局binding实例:你把binding定义为类成员变量,所有ViewHolder都复用这个单一的binding引用。当RecyclerView滚动复用ViewHolder时,只有最后创建的ViewHolder能正确关联binding,其他条目的UI状态都会因为引用旧binding而无法更新,这就是为什么只有极少数条目能正常显示选中状态。
修复方案
- 移除类级别的
binding变量,让每个ViewHolder持有自己独立的binding实例 - 在
onBindViewHolder中通过holder.binding获取当前条目的binding,避免交叉引用 - 优化颜色状态列表的初始化,减少重复创建开销
修改后的适配器代码
package com.example.ui.home.collection.adapter import android.content.Context import android.content.res.ColorStateList import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.example.abc.R import com.example.abc.data.network.responses.success.CollectionCustomersResponse import com.example.abc.databinding.ItemCollectionTypeBinding import com.example.abc.ui.home.collection.adapter.viewHolder.ItemViewHolder class ItemAdapter( private val items: List<CollectionCustomersResponse.Items>, private val context: Context, private var onItemClicked: ((item: CollectionCustomersResponse.Items) -> Unit) ) : RecyclerView.Adapter<ItemViewHolder>() { private var selectedItemID :Long = 0 // 提前初始化颜色状态列表,避免每次绑定重复创建 private val selectedColorState = ColorStateList.valueOf(context.resources.getColor(R.color.main_color)) private val unSelectedColorState = ColorStateList.valueOf(context.resources.getColor(R.color.grey_shade)) override fun onCreateViewHolder( parent: ViewGroup, viewType: Int ): ItemViewHolder { // 每个ViewHolder创建独立的binding实例 val binding = ItemCollectionTypeBinding.inflate(LayoutInflater.from(parent.context), parent, false) return ItemViewHolder(binding) } override fun onBindViewHolder(holder: ItemViewHolder, position: Int) { val item = items[position] val binding = holder.binding // 获取当前ViewHolder专属的binding binding.fab.setOnClickListener { setSelectedItemID(item.CustomerInventoryID) onItemClicked(item) } if (selectedItemID == item.CustomerInventoryID) { binding.fab.setImageResource(R.drawable.item_selected1) binding.fab.backgroundTintList = selectedColorState } else { binding.fab.setImageResource(R.drawable.item_unselected) binding.fab.backgroundTintList = unSelectedColorState } binding.tvItemName.setTextIsSelectable(true) holder.bind(item) } private fun setSelectedItemID (itemID: Long) { this.selectedItemID = itemID notifyDataSetChanged() } override fun getItemCount(): Int = items.size }
配套的ViewHolder实现
确保ViewHolder正确持有自己的binding:
class ItemViewHolder(val binding: ItemCollectionTypeBinding) : RecyclerView.ViewHolder(binding.root) { fun bind(item: CollectionCustomersResponse.Items) { binding.item = item binding.executePendingBindings() } }
内容的提问来源于stack exchange,提问作者Vishnu Satheesh
相关产品推荐
相关产品推荐

