简体   繁体   English

将通用 RecyclerView Adapter 转换为 Kotlin

[英]Converting a generic RecyclerView Adapter to Kotlin

Currently, I have a list I want to populate, but the list items can have different data.目前,我有一个要填充的列表,但列表项可以有不同的数据。 I have two data classes and to accommodate this for the RecyclerView, I have two different ViewHolders extending a basic base ViewHolder.我有两个数据类,为了适应 RecyclerView,我有两个不同的 ViewHolder,扩展了一个基本的基本 ViewHolder。 The different ViewHolders are required as different layouts are used for the different data classes.由于不同的数据类使用不同的布局,因此需要不同的 ViewHolder。

I have converted the view holders to Kotlin, however I have an issue with the adapter.我已将视图持有者转换为 Kotlin,但是我遇到了适配器问题。

The base ViewHolder in Kotlin: Kotlin 中的基本 ViewHolder:

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

A ViewHolder that implements the base ViewHolder in Kotlin:在 Kotlin 中实现基础 ViewHolder 的 ViewHolder:

class StandardViewHolder(view: View): BaseViewHolder<Standard>(view) {

    private val _eventView      : TextView = view.findViewById(R.id.eventTextView)
    private val _dateView       : TextView = view.findViewById(R.id.dateTextView)

    override fun bind(item: Standard) {
        _eventView.text     = item.event
        _dateView.text      = item.date.toString()
    }
}

In Java, I can create an adapter that uses these ViewHolders:在 Java 中,我可以创建一个使用这些 ViewHolders 的适配器:

public class ListAdapter extends RecyclerView.Adapter<BaseViewHolder> {

    private List<Object> _items;
    private Context _context;

    public ListAdapter(List<Object> items, Context context){
        _items      = items;
        _context    = context;
    }

    @NonNull
    @Override
    public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        if(viewType == R.layout.item_standard){
            return new StandardViewHolder(LayoutInflater.from(_context).inflate(
                    R.layout.item_standard, parent, false));
        }

        return new AdvancedViewHolder(LayoutInflater.from(_context).inflate(
                R.layout.item_advanced, parent, false));
    }

    @Override
    @SuppressWarnings("unchecked")
    public void onBindViewHolder(@NonNull BaseViewHolder holder, int position) {
        holder.bind(_items.get(position));
    }

    @Override
    public int getItemCount() {
        return _items.size();
    }

    @Override
    public int getItemViewType(int position) {
        if(_items.get(position) instanceof Standard){
            return R.layout.item_standard;
        }

        return R.layout.item_advanced;
    }
}

However, if I convert this to Kotlin, I get an error on the holder.bind , being:但是,如果我将其转换为 Kotlin,我会在holder.bind上收到一个错误,即:

Out-projected type 'BaseViewHolder<*>' prohibits the use of 'public abstract fun bind(item: T)'外投影类型 'BaseViewHolder<*>' 禁止使用 'public abstract fun bind(item: T)'

class ListAdapter(private val _items: List<Any>, private val _context: Context) : RecyclerView.Adapter<BaseViewHolder<*>>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
        return if (viewType == R.layout.item_standard) {
            StandardViewHolder(LayoutInflater.from(_context).inflate(
                    R.layout.item_standard, parent, false))
        } else {
            AdvancedViewHolder(LayoutInflater.from(_context).inflate(
                    R.layout.item_advanced, parent, false))
        }
    }

    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) = holder.bind(_items[position])

    override fun getItemCount() = _items.size

    override fun getItemViewType(position: Int): Int {
        return if (_items[position] is Standard) {
            R.layout.item_standard
        } else {
            R.layout.item_advanced
        }
    }
}

How do I use these generic ViewHolders in Kotlin?如何在 Kotlin 中使用这些通用 ViewHolders?

The problem is that, even though you know that the item being bound matches the type of the ViewHolder that was created for it, you can't prove it at compile-time, so the compiler yells at you.问题是,即使您知道被绑定的项目与为其创建的 ViewHolder 的类型相匹配,您也无法在编译时证明它,因此编译器会对您大喊大叫。 You can't call bind on BaseViewHolder<*> because then the argument would have to be of type * which can't happen.您不能在BaseViewHolder<*>上调用bind ,因为参数必须是*类型,这不可能发生。 What you need there BaseViewHolder<Any> but you can't make the adapter RecyclerView.Adapter<BaseViewHolder<Any>> because that will break onCreateViewHolder .你需要什么BaseViewHolder<Any>但你不能制作适配器RecyclerView.Adapter<BaseViewHolder<Any>>因为这会破坏onCreateViewHolder And I've tried BaseViewHolder<out Any> , that doesn't work either.我已经尝试过BaseViewHolder<out Any> ,这也不起作用。

So here is what you do: Use BaseViewHolder<*> , but then inside onBindViewHolder cast it to BaseViewHolder<Any> .所以这就是你要做的:使用BaseViewHolder<*> ,然后在onBindViewHolder内部将它转换为BaseViewHolder<Any> The compiler will complain " Hey! That's an unchecked cast! You shouldn't do that! ", so tell it to shut up with @Suppress("UNCHECKED_CAST") .编译器会抱怨“嘿!这是一个未经检查的演员表!你不应该这样做! ”,所以告诉它闭嘴@Suppress("UNCHECKED_CAST")

class ListAdapter(
        private val _items: List<Any>,
        private val _context: Context
) : RecyclerView.Adapter<BaseViewHolder<*>>() {

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder<*> {
        return if (viewType == R.layout.item_standard) {
            StandardViewHolder(LayoutInflater.from(_context).inflate(
                    R.layout.item_standard, parent, false))
        } else {
            AdvancedViewHolder(LayoutInflater.from(_context).inflate(
                    R.layout.item_advanced, parent, false))
        }
    }

    @Suppress("UNCHECKED_CAST")
    override fun onBindViewHolder(holder: BaseViewHolder<*>, position: Int) {
        (holder as BaseViewHolder<Any>).bind(_items[position])
    }

    override fun getItemCount() = _items.size

    override fun getItemViewType(position: Int): Int {
        return if (_items[position] is Standard) {
            R.layout.item_standard
        } else {
            R.layout.item_advanced
        }
    }
}

Nice one… Actually i had some rework on that and see what i've done.. I've made it the most dynamic adapter one can ever make.不错的……实际上我对此进行了一些返工,看看我做了什么..我已经把它做成了有史以来最动态的适配器。

https://medium.com/@sandipsavaliya/tired-with-adapters-4240e5f45c24 https://medium.com/@sandipsavaliya/tired-with-adapters-4240e5f45c24

For Generic Recycler view use the following code:对于 Generic Recycler 视图,请使用以下代码:

Step 1: Extend Adapter with RecyclerView.Adapter() eg第 1 步:使用 RecyclerView.Adapter() 扩展适配器,例如

class MyAdapter(private val recentList: ArrayList<CollectionModel>) :
        RecyclerView.Adapter<**RecyclerView.ViewHolder**>() {
}

Step 2: set View type for all views eg第 2 步:为所有视图设置视图类型,例如

override fun getItemViewType(position: Int): Int {
        return if (recentList[position].type == 0) CollectionModel.COLLECTION_HEADER else CollectionModel.COLLECTION_DETAIL
    }

Step 3: inside onCreateViewHolder define proper view and ViewHolder eg第 3 步:在 onCreateViewHolder 内部定义适当的视图和 ViewHolder 例如

if (viewType == CollectionModel.COLLECTION_HEADER) {
            val view = LayoutInflater.from(parent.context).inflate(R.layout.signle_row_header, parent, false)
            return HeaderCollectionHolder(view)
        } else {
            val view = LayoutInflater.from(parent.context).inflate(R.layout.single_row_collection_history, parent, false)
            return DetailHolder(view)
        }

Step 4: Inside onBindViewHolder bind data based on view type and type cast the view holder using as Keyword eg第 4 步:在 onBindViewHolder 内部根据视图类型绑定数据,并使用as Keyword 例如类型转换视图持有者

val collectionModel = recentList[position]

        if (recentList[position].type == CollectionModel.COLLECTION_HEADER) {
            (holder as HeaderCollectionHolder).apply {
                header.text = collectionModel.date
            }
        } else {
            (holder as DetailHolder).apply {
                name.text = collectionModel.name
                phone.text = collectionModel.phone
                collectedAmount.text = collectionModel.lastCollectAmount.toInt().toString()
                balanceAmount.text = collectionModel.pendingBalance.toInt().toString()
                Utils.setImage(collectionModel.picUrl, imageView)
            }
        }

Step 5: Create two view holder class as below:第 5 步:创建两个视图持有者类,如下所示:

inner class DetailHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    var name: TextView = itemView.findViewById(R.id.name)
    var phone: TextView = itemView.findViewById(R.id.phone)
    var collectedAmount: TextView = itemView.findViewById(R.id.total_amount)
    var balanceAmount: TextView = item`enter code here`View.findViewById(R.id.balance_amount)
    var imageView: CircularImageView = itemView.findViewById(R.id.user_image)

}

inner class HeaderCollectionHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    val header: TextView = itemView.findViewById(R.id.text)
}

Thats it.就是这样。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM