【问题标题】:Kotlin Generics for RecyclerView AdapterRecyclerView 适配器的 Kotlin 泛型
【发布时间】:2019-06-11 03:05:20
【问题描述】:

我正在尝试编写一个通用的 recyclerview 适配器。我找到了几个例子。但是,仍然想不通如何实现通用适配器。我写的代码是,

open abstract class BaseAdapter<T : RecyclerView.ViewHolder>(private val onClickListener: View.OnClickListener, @LayoutRes private val layoutResource:Int) :
        RecyclerView.Adapter<T>() {
        var items: MutableList<Item> = mutableListOf()

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): T  {
            val layout = LayoutInflater.from(parent.context).inflate(layoutResource, parent, false)
            layout.setOnClickListener(onClickListener)
            return T(layout)
        }

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

我在return T(layout) 行遇到错误。错误是Expression 'T' of type 'Int' cannot be invoked as a function. The function 'invoke()' is not found

【问题讨论】:

标签: java android generics kotlin


【解决方案1】:

我对 ListAdapter 的通用 this 使用类似的东西(利用 ViewBinding

class BaseListAdapter<T>(
    private val inflate: (layoutInflater: LayoutInflater, parent: ViewGroup?, attachToParent: Boolean) -> ViewBinding,
    private val bind: (item: T, binding: ViewBinding) -> Unit,
    private val onClick: (item: T) -> Unit,
    compareItems: (old: T, new: T) -> Boolean,
    compareContents: (old: T, new: T) -> Boolean
) : ListAdapter<T, RecyclerView.ViewHolder>(DiffCallback(compareItems, compareContents)) {
    var items = emptyList<T>()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
        ItemViewHolder(inflate(LayoutInflater.from(parent.context), parent, false))

    @Suppress("UNCHECKED_CAST")
    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
        bind(getItem(position), (holder as BaseListAdapter<T>.ItemViewHolder).binding)
    }

    internal fun setItems(items: List<T>) {
        this.items = items
        this.submitList(items)
    }

    inner class ItemViewHolder(val binding: ViewBinding) : RecyclerView.ViewHolder((binding).root) {
        init {
            binding.root.setOnClickListener { onClick(getItem(adapterPosition)) }
        }
    }

    private class DiffCallback<K>(
        private val compareItems: (old: K, new: K) -> Boolean,
        private val compareContents: (old: K, new: K) -> Boolean
    ) : DiffUtil.ItemCallback<K>() {
        override fun areItemsTheSame(old: K, new: K) = compareItems(old, new)
        override fun areContentsTheSame(old: K, new: K) = compareContents(old, new)
    }
}

然后在 Fragment/Activity 中,您可以像这样启动它(CustomerRvCustomerBinding 作为 ViewBinding)

val adapter = BaseListAdapter<Customer>(
    { li, parent, attach -> RvCustomerBinding.inflate(li, parent, attach) },
    { item, vb -> (vb as RvCustomerBinding).tvName.text = item.name },
    { item -> displayToast(requireContext(), "Customer: ${item.name}") },
    { old, new -> old.id == new.id },
    { old, new -> old == new }
)

【讨论】:

  • 我想到了一个类似的解决方案,但我们可以在这里减少更多参数
【解决方案2】:

Kotlin 不允许创建在类型参数中标识的类型的对象(与 C# 不同)。

因此,在您的情况下,您可以在没有 onCreateViewHolder 方法实现的情况下离开抽象类。或者您可以添加另一个抽象方法并在onCreateViewHolder 中调用它。

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): T  {
    val layout = LayoutInflater.from(parent.context).inflate(layoutResource, parent, false)
    layout.setOnClickListener(onClickListener)
    return createHolderInstance(layout)
}

abstract fun createHolderInstance(layout: View): T

这样子类中的代码应该更少。例如:

class UsersAdapter(...) : BaseAdapter<UserHolder>(...) {
    override fun createHolderInstance(layout: View) = UserHolder(layout)
}

【讨论】:

  • 另一种选择是在构造函数中添加一个(View) -&gt; T 参数。
【解决方案3】:

如果有人对 recyclerview 适配器感兴趣

abstract class BaseAdapter<T> : RecyclerView.Adapter<BaseAdapter<T>.BaseViewHolder>() {
    protected val models = mutableListOf<T>()

    fun updateData(list: List<T>) {
        models.clear()
        models.addAll(list)
        notifyDataSetChanged()
    }

    override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
        holder.bind(models[position])
    }

    override fun getItemCount() = models.size

    abstract inner class BaseViewHolder constructor(view: View) : RecyclerView.ViewHolder(view) {
        abstract fun bind(model: T)
    }
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-29
    • 2020-07-08
    相关资源
    最近更新 更多