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

基于ViewBinding的通用RecyclerView Adapter实现问题求解

通用RecyclerView Adapter实现的核心问题与解决方案

问题背景

尝试实现一个通用RecyclerView Adapter,当前代码如下:

class RVAdapter <T: Any, VB: ViewBinding>(
  private var dataSet: List<T>,
  private val bindingInterface: RVBinding<T, VB>
) : ListAdapter<T, RVAdapter<T, VB>.RVHolder>(DiffCallback<T>()) {

  inner class RVHolder(private val binding: ViewBinding): RecyclerView.ViewHolder(binding.root) {
    fun<T: Any, VB: ViewBinding> bind(item: T, bindingInterface: RVBinding<T, VB>) {
      bindingInterface.bind(item, (binding as VB)) // 未检查的类型转换
    }
  }

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RVHolder {
    val binding = (VB as ViewBinding).inflate() // 问题1:如何通过泛型VB填充布局?

    return RVHolder(binding)
  }

  override fun onBindViewHolder(holder: RVHolder, position: Int) {
    holder.bind(getItem(position), bindingInterface)
  }

  override fun getItemCount() = dataSet.size
}

private class DiffCallback<T: Any>: DiffUtil.ItemCallback<T>() {
  // 问题2:如何通用实现这两个方法?
  override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
    TODO("Not yet implemented")
  }

  override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
    return oldItem == newItem
  }
}

interface RVBinding<T: Any, VB: ViewBinding> {
  fun bind(item: T, binding: VB)
}

核心问题

  • 如何在onCreateViewHolder()中实例化泛型VB类型的ViewBinding?常规的SpecificBinding.inflate()方式在泛型场景下无法直接使用;
  • 如何以通用方式实现DiffUtil.ItemCallback的areItemsTheSame和areContentsTheSame方法?直接比较toString或==(依赖equals实现)的方式不具备通用性。

解决方案

1. 泛型ViewBinding的实例化解决

由于Kotlin泛型的类型擦除特性,无法直接通过VB类调用inflate方法,推荐将布局创建逻辑通过构造参数传入,同时优化ViewHolder消除类型转换警告:

class RVAdapter <T: Any, VB: ViewBinding>(
  private val bindingFactory: (LayoutInflater, ViewGroup, Boolean) -> VB,
  private val bindItem: (T, VB) -> Unit,
  diffCallback: DiffUtil.ItemCallback<T>
) : ListAdapter<T, RVAdapter<T, VB>.RVHolder>(diffCallback) {

  inner class RVHolder(private val binding: VB): RecyclerView.ViewHolder(binding.root) {
    fun bind(item: T) {
      bindItem(item, binding)
    }
  }

  override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RVHolder {
    val inflater = LayoutInflater.from(parent.context)
    val binding = bindingFactory(inflater, parent, false)
    return RVHolder(binding)
  }

  override fun onBindViewHolder(holder: RVHolder, position: Int) {
    holder.bind(getItem(position))
  }
}

使用时传入Binding的创建逻辑:

val adapter = RVAdapter(
  { inflater, parent, attach -> ItemBinding.inflate(inflater, parent, attach) },
  { item, binding -> binding.tvName.text = item.name },
  // 后面会补充DiffCallback的实现
)

2. 通用DiffUtil.ItemCallback实现

提供两种通用方案,按需选择:

方案一:定义接口约束数据类

让所有数据类实现统一接口,提供唯一标识和内容比较能力:

interface Diffable {
  fun getUniqueId(): Any
  fun contentEquals(other: Diffable): Boolean
}

class DiffCallback<T: Diffable>: DiffUtil.ItemCallback<T>() {
  override fun areItemsTheSame(oldItem: T, newItem: T): Boolean {
    return oldItem.getUniqueId() == newItem.getUniqueId()
  }

  override fun areContentsTheSame(oldItem: T, newItem: T): Boolean {
    return oldItem.contentEquals(newItem)
  }
}

数据类实现示例:

data class Item(val id: String, val name: String): Diffable {
  override fun getUniqueId(): Any = id
  override fun contentEquals(other: Diffable): Boolean {
    return other is Item && this == other
  }
}

方案二:构造时传入判断逻辑

无需修改数据类,直接在Adapter构造时传入两个lambda处理Diff判断:

// 创建Adapter时直接传入Diff逻辑
val adapter = RVAdapter(
  { inflater, parent, attach -> ItemBinding.inflate(inflater, parent, attach) },
  { item, binding -> binding.tvName.text = item.name },
  object : DiffUtil.ItemCallback<Item>() {
    override fun areItemsTheSame(oldItem: Item, newItem: Item): Boolean {
      return oldItem.id == newItem.id
    }

    override fun areContentsTheSame(oldItem: Item, newItem: Item): Boolean {
      return oldItem == newItem
    }
  }
)

其他优化建议

  • 移除getItemCount重写:ListAdapter已通过currentList实现该方法,无需重复编写;
  • 使用submitList更新数据:ListAdapter依赖此方法感知数据变化,不要直接修改dataSet;
  • 简化绑定逻辑:将RVBinding接口替换为lambda,进一步精简代码(如上述示例所示)。

内容的提问来源于stack exchange,提问作者Sorry

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.06 13:30:42