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

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而无法更新,这就是为什么只有极少数条目能正常显示选中状态。

修复方案

  1. 移除类级别的binding变量,让每个ViewHolder持有自己独立的binding实例
  2. 在onBindViewHolder中通过holder.binding获取当前条目的binding,避免交叉引用
  3. 优化颜色状态列表的初始化,减少重复创建开销

修改后的适配器代码

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 04:30:41