美文网首页Kotlin我的Kotlin之旅kotlin
Kotlin实战(二): 实现RecyclerView多种Ite

Kotlin实战(二): 实现RecyclerView多种Ite

作者: 叫我旺仔 | 来源:发表于2017-06-09 18:05 被阅读784次

    前言

    RecyclerView出来很久了,可以说一出来就将ListView给比下去了,当然,Recyclerview有它的好,ListView的好,并不是说一定要用Recyclerview,最适用自己项目的才是最好的。

    在这里我们将用Kotlin来实现RecyclerView的多种item布局,和单个item布局,同时写一个通用的Adapter。

    使用

    先将写完的代码的使用方式展示一下:

    一种item布局

    class SingleItemAdapter(mContext: Context, mDatas: List<TestBean>)
        : DelegateItemAdapter<TestBean>(mContext, mDatas) {
        init {
            addItemViewDelegate(SingleItemDelegate())
        }
    }
    

    效果图

    SingleItem

    多种item布局

    class MultiItemAdapter(mContext: Context, mDatas: List<TestBean>) 
        : DelegateItemAdapter<TestBean>(mContext, mDatas) {
        init {
            addItemViewDelegate(LeftDelegate())
            addItemViewDelegate(CenterDelegate())
            addItemViewDelegate(RightDelegate())
        }
    }
    

    效果图

    MultiItem

    梳理

    总体流程是这样的,首先创建itemView,在里面设置layoutId和数据处理,然后创建一个类继承DelegateItemAdapter,并在主构造方法里面添加不同的itemView,然后Adapter通过DelegateManager类来管理对应的itemView进行操作。

    ItemView

    我们的itemView是实现DelegateType接口,然后在里面设置相对应的layoutId,对数据进行操作处理:

    class SingleItemDelegate : DelegateType<TestBean> {
        override val itemViewLayoutId: Int
            get() = R.layout.item_left
    
        override fun isItemViewType(item: TestBean, position: Int): Boolean = true
    
        override fun convert(context: Context, holder: ViewHolder, item: TestBean, position: Int) {
            with(holder.itemView) {
                item_left_text.text = item.text
                setOnClickListener {
                    context.toast("SingleItemDelegate")
                }
            }
        }
    }
    

    ViewHolder

    在使用Recyclerview的时候,必须有ViewHolder,通常情况下,我们需要写一个通用的ViewHolder,但是在Kotlin中的话,就不需要那样写,因为Kotlin可以直接将布局的id来当成变量使用,所以我们需要写一个ViewHolder类来继承Recyclerview里面的Viewholder类。

    使用id当变量的话,要在app下面的build.gradle里面加个plugin: 'kotlin-android-extensions'

    extensions
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
    
        companion object {
            /**
             * 创建ViewHolder
             *
             * @param itemView itemView
             * @return ViewHolder
             */
            fun createViewHolder(itemView: View): ViewHolder {
                val holder = ViewHolder(itemView)
                return holder
            }
    
            /**
             * 创建ViewHolder
             *
             * @param context Context
             * @param parent ViewGroup
             * @param layoutId layoutId
             * @return ViewHolder
             */
            fun createViewHolder(context: Context,
                                 parent: ViewGroup, layoutId: Int): ViewHolder {
                val itemView = LayoutInflater.from(context).inflate(layoutId, parent,
                        false)
                val holder = ViewHolder(itemView)
                return holder
            }
        }
    
    }
    

    这样子ViewHolder就搞定了,不需要在写那些设置text、image、绑定事件相关的代码了。

    Adapter

    那么我们改如何区分多种itemView呢,在这里我们引用了一个接口:

    /**
     * itemView属性
     */
    interface DelegateType<in T> { // 由于T只是作为参数,所以T是contravariant逆变,加in
        /**
         * 获取layoutId
         */
        val itemViewLayoutId: Int
    
        /**
         * 判断类型
         *
         * @param item data数据
         * @param position 当前position
         * @return true显示数据
         */
        fun isItemViewType(item: T, position: Int): Boolean
    
        /**
         * 显示数据
         *
         * @param context Context
         * @param holder ViewHolder
         * @param item data数据
         * @param position 当前position
         */
        fun convert(context: Context, holder: ViewHolder, item: T, position: Int)
    }
    

    根据layoutId来创建对应的ViewHolder,并且通过isItemViewType方法来匹配是否是当前itemView类型,然后通过convert来显示数据。

    Adapter是继承Recyclerview里面的Adapter,传入ViewHolder,传入泛型数据集,那么我们可以通过传入的数据集里面的类型来判断不同的itemView,在这里需要重写几个方法:

    getItemViewType方法
    根据数据来返回不同类型:

    /**
     * 获取itemView类型
     *
     * @param position 当前position
     */
    override fun getItemViewType(position: Int): Int = if (!useItemViewDelegateManager())
        super.getItemViewType(position)
    else
        mDelegateManager.getItemViewType(mDatas[position], position)
    

    onCreateViewHolder方法
    根据mDelegateManager.getItemViewDelegate(viewType).itemViewLayoutId返回的layoutId来生成对应的ViewHolder

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder = ViewHolder.createViewHolder(mContext, parent, mDelegateManager.getItemViewDelegate(viewType).itemViewLayoutId)
    

    onBindViewHolder方法
    用于数据处理和显示:

    override fun onBindViewHolder(holder: ViewHolder, position: Int) = convert(holder, mDatas[position])
    

    上面的mDelegateManager是管理itemView的类,可以添加删除itemView,并且显示数据:

    /**
    * 获取itemView的类型
    *
    * @param item data数据
    * @param position 当前position
    * @return Int
    */
    fun getItemViewType(item: T, position: Int): Int {
        (0.. delegateCount - 1)
                .filter { mDelegates.valueAt(it).isItemViewType(item, position) }
                .forEach { return mDelegates.keyAt(it) }
        throw IllegalArgumentException(
                "No ItemViewDelegate added that matches position = $position in data = $item source");
    }
    
    /**
    * 显示数据
    *
    * @param context Context
    * @param holder ViewHolder
    * @param item data数据
    * @param position 当前position
    */
    fun convert(context: Context, holder: ViewHolder, item: T, position: Int) {
        for (i in 0..delegateCount - 1) {
            val delegate = mDelegates.valueAt(i)
            if (delegate.isItemViewType(item, position)) {
                delegate.convert(context, holder, item, position);
                return@convert
            }
        }
        throw IllegalArgumentException(
                "No ItemViewDelegateManager added that matches position= $position in data = $item source")
    }
    
    /**
    * 添加itemView
    *
    * @param delegate itemView
    * @throws IllegalArgumentException 已存在不能再添加
    * @return DelegateManager
    */
    fun addDelegate(delegate: DelegateType<T>): DelegateManager<T> {
        for (i in 0..delegateCount - 1) {
            if (mDelegates.valueAt(i).itemViewLayoutId == delegate.itemViewLayoutId) {
                throw IllegalArgumentException("An ItemViewDelegate is already registered for the delegate = $delegate.")
            }
        }
        mDelegates.put(mDelegates.size(), delegate)
        return this
    }
    
    /**
    * 添加itemView
    *
    * @param viewType 代表itemView的下标
    * @param delegate itemView
    * @throws IllegalArgumentException 已存在不能再添加
    * @return DelegateManager
    */
    fun addDelegate(viewType: Int, delegate: DelegateType<T>): DelegateManager<T> {
        if (mDelegates.get(viewType) != null) {
            throw IllegalArgumentException("An ItemViewDelegate is already registered for the viewType = $viewType. Already registered ItemViewDelegate is ${mDelegates.get(viewType)}")
        }
        mDelegates.put(viewType, delegate)
        return this
    }
    
    /**
     * 获取itemView
     *  
     * @param viewType 代表itemView的下标
     * @return DelegateType
     */
    fun getItemViewDelegate(viewType: Int): DelegateType<T> = mDelegates.get(viewType)
    
    /**
     * 获取itemView的LayoutId
     *
     * @param viewType 代表itemView的下标
     * @return Int
    */
    fun getItemViewLayoutId(viewType: Int): Int = getItemViewDelegate(viewType).itemViewLayoutId
    
    /**
    * 获取itemView的类型
    *
    * @param delegate itemView
    * @return Int
    */
    fun getItemViewType(delegate: DelegateType<T>): Int = mDelegates.indexOfValue(delegate)
    
    

    通过使用addDelegate方法添加itemView,然后通过getItemViewType方法来获取itemView的类型,最后通过convert方法来进行数据操作和显示,这样子我们的Adapter就写出来了。

    总结

    每次写Recyclerview的时候都要重复写这些Adapter,ViewHolder的代码,这里将它写成通用的,可以省去很多的时间。

    注意:项目是在3.0版本的Android Studio上运行。

    源码:Github,欢迎star。

    参考:为RecyclerView打造通用Adapter 让RecyclerView更加好用

    相关文章

      网友评论

        本文标题:Kotlin实战(二): 实现RecyclerView多种Ite

        本文链接:https://www.haomeiwen.com/subject/jncfqxtx.html