首页 > 解决方案 > ListAdapter 中的 Kotlin 泛型问题

问题描述

我正在尝试为 RecyclerView 纠正一个通用 ListAdapter。我有 3 件事要传递给适配器。项目列表、要使用的行布局和 ViewHolder。我能够获得一般的列表和布局,但它是 ViewHolder。这是我到目前为止所拥有的,但我对 Kotlin 的泛型仍然很陌生。我尝试使用 Class out 方法,然后在调用特定视图的构造函数时遇到问题。

abstract class AbstractListAdapter(
    private val items: List<*>, 
    private val layoutId: Int, 
    private val viewHolderClass: ???? >
) : RecyclerView.Adapter<RecyclerView.ViewHolder>(){

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val v = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
        return viewHolderClass.??? // need to call the constructor of the specific viewholder passed in
    }

    override fun getItemId(position: Int): Long {
        return position.toLong()
    }

    override fun getItemViewType(position: Int): Int {
        return position
    }

    override fun getItemCount(): Int {
        return items.size
    }

}

// List adapter uses abstract
class ListAdapter(private items: List<Files>, private val id: Int, private viewHolder: FileViewHolder) : AbstractListAdapter(items, id, fileViewHolder) {

    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
             ... some code here for the viewholder and list combining
    }
}

// Specific VH that want to pass into abstract generic yet it will call the constructor from there.
class FileViewHolder(fileView: View) {
   .... grab views for specific layout
}

标签: androidgenericskotlinandroid-recyclerviewandroid-listadapter

解决方案


您可以通过采用 ViewHolder 构造函数而不是类来做到这一点。这样您就不必使用反射来实例化 ViewHolder。您需要拥有 ViewHolder 的泛型类型,以便您的子类可以正确实现onBindViewHolder并访问特定类型的 ViewHolder。

此外,您必须使items属性具有类型,否则您将无法使用它。而且您的子类可能需要能够访问它,因此它需要受到保护,而不是私有。

我没有对此进行测试:

abstract class AbstractListAdapter<VH: RecyclerView.ViewHolder> (
    protected val items: List<*>, 
    private val layoutId: Int, 
    private val viewHolderConstructor: (View) -> VH >
) : RecyclerView.Adapter<VH>(){

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
        val v = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
        return viewHolderConstructor(v)
    }

    //...
}

然后为了实现它,在超构造函数调用中指定 ViewHolder 构造函数并指定类型。由于指定了类型,因此您不需要在子类中为其提供构造函数参数。

class ListAdapter(private items: List<Files>, private val id: Int) : AbstractListAdapter<FileViewHolder>(items, id, ::FileViewHolder) {

    override fun onBindViewHolder(holder: FileViewHolder, position: Int) {
        //...
    }
}

也就是说,我不确定这实际上为您带来了什么。似乎只是在移动代码。您仍然需要从父视图中提取视图引用。您只需在 ViewHolder 初始化中而不是在onCreateViewHolder. 现在您必须小心地传递与特定 ViewHolder 类型匹配的正确布局。您也可以删除该参数并在 ViewHolder 构造函数中进行布局以避免该问题。但是现在您所做的只是将onCreateViewHolder功能移到 ViewHolder 的init块中。

此外,您的抽象类版本正在颠覆您已覆盖的函数的预期结果。为什么列表中的每个项目都有不同的类型?为什么项目 ID 会基于列表位置?这只会破坏编辑列表数据的功能(重新排列、添加和删除将被破坏)。


推荐阅读