具有泛型的多种视图类型的RecyclerView

时间:2019-07-11 11:30:51

标签: android kotlin android-recyclerview recycler-adapter

我正在尝试使用通用基本视图持有者在回收器视图中实现多种视图类型。

BaseViewHolder类是

abstract class BaseViewHolder<T>(itemView: View) : RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}

然后我为特定视图类型创建了一个视图持有人类

inner class CarouselViewHolder(itemView: View) : BaseViewHolder<HomeCarousel>(itemView) {
    override fun bind(item: HomeCarousel) {
    }
}

从有趣的onBindViewHolder方法访问此绑定函数时遇到错误。

投影类型为'BaseViewHolder <*>'的项目禁止使用'公共抽象有趣的绑定(item:T):

 override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
    val element = data[position]
    when (element) {
        is HomeCarousel -> {
            holder.bind(element)
        }
        else -> throw java.lang.IllegalArgumentException("Invalid binding")
    }
}

出现错误

       holder.bind(element)

这是我的整个Adapter类

class HomePageRecylerAdapter(private val data: ArrayList<Any>) : RecyclerView.Adapter<BaseViewHolder<*>>() {

companion object {
    const val typeCarousel = 0
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
    return when (viewType) {
        typeCarousel -> {
            val view =
                LayoutInflater.from(parent.context).inflate(R.layout.recycler_item_home_carasoul, parent, false)
            CarouselViewHolder(view)
        }
        else -> throw IllegalArgumentException("Invalid view type")
    }

}

override fun getItemCount() = data.size

override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
    val element = data[position]
    when (element) {
        is HomeCarousel -> {
            holder.bind(element)
        }
        else -> IllegalArgumentException("Invalid binding")
    }
}



override fun getItemViewType(position: Int): Int {
    val element = data[position]
    return when (element) {
        is HomeCarousel -> typeCarousel
        else -> throw IllegalArgumentException("Invalid type of data {$position}")
    }
}
inner class CarouselViewHolder(itemView: View) : BaseViewHolder<HomeCarousel>(itemView) {
    override fun bind(item: HomeCarousel) {
    }
}
}
abstract class BaseViewHolder<T>(itemView: View) : 
RecyclerView.ViewHolder(itemView) {
   abstract fun bind(item: T)
} 

1 个答案:

答案 0 :(得分:1)

TLDR;

嗯,这是应该起作用的代码:

class HomePageRecylerAdapter(private val data: ArrayList<Any>) : RecyclerView.Adapter<BaseViewHolder<Any>>() {

    companion object {
        const val typeCarousel = 0
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<Any> {
        return when (viewType) {
            typeCarousel -> {
                val view =
                        LayoutInflater.from(parent.context).inflate(0, parent, false)
                CarouselViewHolder(view) as BaseViewHolder<Any>
            }
            else -> throw IllegalArgumentException("Invalid view type")
        }

    }

    override fun getItemCount() = data.size

    override fun onBindViewHolder(holder: BaseViewHolder<Any>, position: Int) {
        val element = data[position]
        when (element) {
            is HomeCarousel -> {
                holder.bind(element)
            }
            else -> IllegalArgumentException("Invalid binding")
        }
    }



    override fun getItemViewType(position: Int): Int {
        val element = data[position]
        return when (element) {
            is HomeCarousel -> typeCarousel
            else -> throw IllegalArgumentException("Invalid type of data {$position}")
        }
    }
    inner class CarouselViewHolder(itemView: View) : BaseViewHolder<HomeCarousel>(itemView) {
        override fun bind(item: HomeCarousel) {
        }
    }
}
abstract class BaseViewHolder<in T: Any>(itemView: View) :
        RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}

说明

可能的答案可能有助于您获得更多见解-https://stackoverflow.com/a/51110484/2674983

尽管如此,我发现答案中的理由是错误的。问题不在于适配器是用Java编写的,而是它既需要返回Viewholder的子类型,又需要将其作为参数传递。否则,使用use-site variance之类的东西就很容易。

我认为解决这一问题的正确方法是使用逆方差:

abstract class BaseViewHolder<in T>(itemView: View) :
        RecyclerView.ViewHolder(itemView) {
    abstract fun bind(item: T)
}

这是因为T类型的对象可以执行任何操作,并且需要传递给setter,因此我们只需要将其设置为ViewHolder。但是,对立事物有一个有趣的转折……子类型化现在朝相反的方向起作用!因此,现在BaseViewHolder<HomeCarousel>不再是BaseViewHolder<Any>的子类型,因为子类型是相反的。

要解决此问题,我们可以像代码中那样进行“不安全的转换”。

哦,并且使用BaseViewHolder<*>(星型投影)确实可以解决子类型问题,因为BaseViewHolder的每个实例现在都是一个子类型,但是它同时限制了协方差和协方差,因此在这里没有用。