将通用 RecyclerView Adapter 转换为 Kotlin

one*_*dan 1 java android kotlin

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

我已将视图持有者转换为 Kotlin,但是我遇到了适配器问题。

Kotlin 中的基本 ViewHolder:

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

在 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()
    }
}
Run Code Online (Sandbox Code Playgroud)

在 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

但是,如果我将其转换为 Kotlin,我会在holder.bind上收到一个错误,即:

外投影类型 '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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

Leo*_*Aso 5

问题是,即使您知道被绑定的项目与为其创建的 ViewHolder 的类型相匹配,您也无法在编译时证明它,因此编译器会对您大喊大叫。你不能打电话bindBaseViewHolder<*>因为这样参数就必须是*不可能发生的类型。你在那里需要什么,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
        }
    }
}
Run Code Online (Sandbox Code Playgroud)