如何实现可复用通用RecyclerView Adapter?多模型适配问题求助
实现支持多数据类型的通用RecyclerView Adapter解决方案
当然可以实现这种适配多数据类型的通用RecyclerView Adapter!你遇到的构造函数问题,本质是Kotlin泛型擦除导致的签名冲突,再加上不同数据类型直接传递的类型不兼容问题,咱们一步步来解决:
先说说你原来代码的问题
你写的第二个构造函数尝试直接把ArrayList<Category>传给需要ArrayList<CategoryImage>的主构造函数,这本身就有类型不匹配的问题;而且Kotlin在编译时会擦除泛型信息,两个构造函数的实际签名都会变成(ArrayList<*>, Int),编译器自然会判定签名重复,报错也就理所当然了。
通用Adapter的正确实现方式
我们可以用泛型+高阶函数的方式打造真正通用的Adapter,既支持任意数据类型,又能解耦数据绑定逻辑:
1. 改造后的通用Adapter代码
import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView class RecyclerViewAdapter<T>( private val dataList: List<T>, private val layoutType: Int, // 替代你原来的fragment参数,用来区分布局 private val bindItem: (ViewHolder, T) -> Unit ) : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() { // 通用ViewHolder class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { // 根据layoutType加载对应的item布局 val layoutId = when(layoutType) { 1 -> R.layout.item_category_image // 替换成你的CategoryImage布局ID 2 -> R.layout.item_category // 替换成你的Category布局ID else -> throw IllegalArgumentException("不支持的布局类型") } val itemView = LayoutInflater.from(parent.context) .inflate(layoutId, parent, false) return ViewHolder(itemView) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { // 调用外部传入的绑定逻辑 bindItem(holder, dataList[position]) } override fun getItemCount(): Int = dataList.size }
2. 如何使用这个通用Adapter
- 加载
CategoryImage列表时:
val imageList: ArrayList<CategoryImage> = // 你的数据源 val imageAdapter = RecyclerViewAdapter<CategoryImage>(imageList, 1) { holder, item -> // 在这里写CategoryImage的绑定逻辑,比如给ImageView设置图片 val imageView = holder.itemView.findViewById<ImageView>(R.id.iv_category_image) imageView.load(item.getUrl()) // 这里假设你用了图片加载库,比如Glide/Picasso }
- 加载
Category列表时:
val categoryList: ArrayList<Category> = // 你的数据源 val categoryAdapter = RecyclerViewAdapter<Category>(categoryList, 2) { holder, item -> // 在这里写Category的绑定逻辑,比如设置名称和缩略图 val nameTv = holder.itemView.findViewById<TextView>(R.id.tv_category_name) val thumbnailIv = holder.itemView.findViewById<ImageView>(R.id.iv_category_thumbnail) nameTv.text = item.name thumbnailIv.load(item.thumbnail) }
另一种可选方案:让数据类实现统一接口
如果不想用高阶函数,也可以让两个数据类实现同一个空接口,在Adapter里通过类型判断来绑定数据:
1. 定义统一接口
interface ListItem
2. 修改数据类实现接口
class CategoryImage : ListItem { // 原来的代码不变 } class Category : ListItem { // 原来的代码不变 }
3. 对应的Adapter实现
class RecyclerViewAdapter( private val dataList: List<ListItem>, private val layoutType: Int ) : RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() { class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val layoutId = when(layoutType) { 1 -> R.layout.item_category_image 2 -> R.layout.item_category else -> throw IllegalArgumentException("不支持的布局类型") } val itemView = LayoutInflater.from(parent.context) .inflate(layoutId, parent, false) return ViewHolder(itemView) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { when(val item = dataList[position]) { is CategoryImage -> { // 绑定CategoryImage数据 val imageView = holder.itemView.findViewById<ImageView>(R.id.iv_category_image) imageView.load(item.getUrl()) } is Category -> { // 绑定Category数据 val nameTv = holder.itemView.findViewById<TextView>(R.id.tv_category_name) val thumbnailIv = holder.itemView.findViewById<ImageView>(R.id.iv_category_thumbnail) nameTv.text = item.name thumbnailIv.load(item.thumbnail) } } } override fun getItemCount(): Int = dataList.size }
总结
第一种泛型+高阶函数的方式更灵活,数据绑定逻辑完全由调用方控制,Adapter真正做到了通用;第二种方式更适合数据类型固定的场景。两种方案都能解决你之前遇到的构造函数签名冲突问题,完全满足你的需求。
内容的提问来源于stack exchange,提问作者Armen Hovhannisian
相关产品推荐
相关产品推荐

