将通用的RecyclerView适配器转换为Kotlin

时间:2018-06-28 16:09:44

标签: java android kotlin

当前,我有一个要填充的列表,但是列表项可以具有不同的数据。我有两个数据类,为了适应RecyclerView,我有两个不同的ViewHolder扩展了基本的基本ViewHolder。需要不同的ViewHolders,因为不同的布局用于不同的数据类。

我已将视图持有人转换为Kotlin,但是适配器存在问题。

Kotlin中的基本ViewHolder:

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

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

在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;
    }
}

但是,如果将其转换为Kotlin,则会在 holder.bind 上出现错误,即:

  

投影类型'BaseViewHolder <*>'禁止使用'public   抽象有趣的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
        }
    }
}

如何在Kotlin中使用这些通用ViewHolders?

3 个答案:

答案 0 :(得分:3)

问题是,即使您知道绑定的项目与为其创建的ViewHolder的类型匹配,您也无法在编译时证明它,因此编译器会对您大吼大叫。您不能在bind上调用BaseViewHolder<*>,因为那样的话,该参数必须为*类型,这是不可能发生的。您在那里BaseViewHolder<Any>所需要的东西,但是您无法制造适配器RecyclerView.Adapter<BaseViewHolder<Any>>,因为那样会破坏onCreateViewHolder。而且我已经尝试过BaseViewHolder<out Any>,但这也不起作用。

因此,您可以执行以下操作:使用BaseViewHolder<*>,然后在onBindViewHolder内部将其强制转换为BaseViewHolder<Any>。编译器会抱怨“ 嘿!这是未经检查的强制转换!您不应该这样做!”,因此告诉它使用@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
        }
    }
}

答案 1 :(得分:0)

好人……实际上,我对此进行了一些修改,看看我做了什么。 我使它成为有史以来最动态的适配器。

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

答案 2 :(得分:0)

对于通用回收站视图,请使用以下代码:

第1步:使用RecyclerView.Adapter()扩展适配器 例如

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

步骤2:为所有视图设置视图类型 例如

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

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

第4步:在内部onBindViewHolder中,根据视图类型绑定数据,并使用 as 关键字类型强制转换视图持有者 例如

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

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

就这样。