android - RecyclerView 具有多种 View 类型和泛型

标签 android kotlin android-recyclerview

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

BaseViewHolder 类是

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

然后我为特定 View 类型创建了一个 View 持有者类

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

从 fun onBindViewHolder 方法访问此绑定(bind)函数时出现错误。

投影类型“BaseViewHolder<*>”禁止使用“public abstract fun bind(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)

这是我的整个适配器类

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)
} 

最佳答案

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<*> (star-projection) 确实解决了子类型问题,因为 BaseViewHolder 的每个实例现在都是子类型,但它施加了协变和逆变的限制,因此在这里没有用。

关于android - RecyclerView 具有多种 View 类型和泛型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56988188/

相关文章:

java - 如何通过 api url 索引使用 recyclerview 实现分页

android - 在回收站 View 上快速滚动并无错误地崩溃

android - 如何将可绘制文件夹中的图像放入 HTML 文件

android - 最适合开发的 Android 平板电脑

android - Jetpack Compose 开关切换高度

java - 膨胀 android.support.v7.widget.CardView 时出错

android - 如何解决 "recyclerView must not be null"

java - 在 Android map 上绘制地理上准确的弧线

android - 使用 MediaRecorder 类时如何更改帧率

spring-boot - Kotlin copy() 函数正在 JPA 实体上创建一个新的 Id,从而产生一个新行