超级封装RecyclerView的适配器Adapter 只需二三十行代码

前言

android开发中,RecyclerView是很常用的控件,而且功能也很强大,并且各种三方封装或者扩展库也是非常多,如:BaseQuickAdapter,XRecyclerview,当然还有我以前封装的LtRecyclerView

比如BaseQuickAdapter虽然封装的非常方便,但那是相对于java语言,那用kotlin能不能使Adapter的封装更方便呢?答案是可以

第一次简单封装

/**
 * 封装适配器,适用于单条目
 *
 * @param T bean类的泛型
 * @param VH ViewHolder的泛型
 */
abstract class BaseAdapterOneType<T, VH : RecyclerView.ViewHolder>(val list: MutableList<T>) : RecyclerView.Adapter<VH>() {
    /**
     * 给view设置数据
     */
    abstract fun setData(b: T, i: Int, h: VH)

    override fun onBindViewHolder(holder: VH, position: Int) = setData(list[position], position, holder)

    override fun getItemCount() = list.size
}

使用起来需要继承该类,并重写setData方法和写一个ViewHolder,如下:

    fun initView(){
        .....
        rv.adapter = MainAdapter(arrayListOf())//设置适配器
    }

    class MainAdapter(list: MutableList<String>) : BaseAdapterOneType<String, MainAdapterVH>(list) {
        override fun onCreateViewHolder(p0: ViewGroup, p1: Int): MainAdapterVH = MainAdapterVH(LayoutInflater.from(p0.context).inflate(R.layout.item_pop_text, p0, false))//设置布局

        override fun setData(b: String, i: Int, h: MainAdapterVH) {//设置数据
            h.tv.text = b
        }
    }

    class MainAdapterVH(val v: View) : RecyclerView.ViewHolder(v) {//查找View
        val tv = v.tv1
    }

但是这样和java差不多,写起来还是比较麻烦

第二次封装,简少所需代码

abstract class BaseAdapterOneType3<T>(val list: MutableList<T>, @LayoutRes val layoutId: Int) : RecyclerView.Adapter<BaseViewHolder>() {
    /**
     * 给view设置数据
     */
    abstract fun setData(v: View, b: T, i: Int, h: BaseViewHolder)

    override fun onBindViewHolder(holder: BaseViewHolder, position: Int) = setData(holder.itemView, list[position], position, holder)

    override fun getItemCount() = list.size

    override fun onCreateViewHolder(p0: ViewGroup, p1: Int): BaseViewHolder = BaseViewHolder(LayoutInflater.from(p0.context).inflate(layoutId, p0, false))
}

class BaseViewHolder(private val view: View) : RecyclerView.ViewHolder(view)

使用起来就很方便了,只需要重写setData方法,如下:

    fun initView(){
        ...
        rv.adapter = MainAdapter(arrayListOf())
    }

    class MainAdapter(list: MutableList<String>) : BaseAdapterOneType3<String>(list, R.layout.item_pop_text) {
        override fun setData(v: View, b: String, i: Int, h: BaseViewHolder) {
            //利用Kotlin的特性,直接使用id来查找控件
            v.tv1.text = b
        }
    }

这样封装后用起来就方便的多了

等等,你以为这样就结束了吗?看性能!

我们来看一下MainAdapter编译后的字节码做了什么

ps:通过Koltin自带的插件,然后点击Decompile按钮来还原成java代码

如下:

首先构造和下面的泛型转换的setData方法不用看,检查Null的也不用看,主要看下面两行

TextView var10000 = (TextView)v.findViewById(id.tv1);
var10000.setText((CharSequence)b);

通过上面的代码会发现,每次走setData(onBindViewHolder)都会进行findViewById,如果是一个经常滚动的列表,则会频繁的调用setData方法,则没有用到RecyclerView.ViewHolder,效率变低

中间我想过,参考Activity的方式在公用的ViewHolder里维护一个HashMap<Int,View>,并提供一个get方法,但是这样跟BaseQuickAdapter的写法就一样了,比较繁琐,pass

后来又想,通过by委托给一个类,让其存储键值对和泛型信息,但是后来发现并不行,直到有一次灵光一现!

第三次封装,既少写代码又性能ok

abstract class BaseLtAdapterOneType<T>(var list: MutableList<T>, @LayoutRes private val itemLayoutId: Int) : RecyclerView.Adapter<BaseLtViewHolder>() {
    abstract fun setData(v: BaseLtViewHolder.ViewFind, b: T, i: Int, h: BaseLtViewHolder)

    override fun onBindViewHolder(holder: BaseLtViewHolder, position: Int) = setData(holder.viewFind, list[position], position, holder)

    override fun getItemCount() = list.size

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = BaseLtViewHolder(parent.inflate(itemLayoutId))
}

/**
 * 使用方便的ViewHolder
 */
class BaseLtViewHolder(private val view: View) : RecyclerView.ViewHolder(view) {
    val viewFind: ViewFind = ViewFind().apply { this.setView([email protected]) }

    /**
     * 使用kt的框架来快捷查找view,并且带有缓存
     */
    class ViewFind : Fragment() {
        private lateinit var mView: View

        fun setView(view: View) {
            this.mView = view
        }

        override fun getView(): View? {
            return mView
        }
    }
}

实现思路,有一次我想,既然Fragment中能用Kotlin的框架直接来findViewById,那能不能找找是从哪个方法获取父View的,然后发现原来是getView这个方法,而且还可以进行复写,于是就给RecyclerView.ViewHolder的itemView加了一层Fragment,然后在setData中用Fragment来查找View,这样既写着方便,并且在Fragment中还有缓存(Kotlin框架的实现),简直完美

使用方法如下:

    fun initView(){
        ...
        rv.adapter = MainAdapter(arrayListOf())
    }

    class MainAdapter(list: MutableList<String>) : BaseLtAdapterOneType<String>(list, R.layout.item_pop_text) {
        override fun setData(v: BaseLtViewHolder.ViewFind, b: String, i: Int, h: BaseLtViewHolder) {
            //在这里直接通过Fragment来查找View,并设置属性
            v.tv1.text = b
        }
    }

并且查看编译后的代码确实有缓存:

利用IDEA的Live Templates来快捷生成代码

有的同学说,还是很多样板代码,不想写怎么办,emmm...有的办,使用Live Templates

打开设置,根据图的步骤添加

起一个名字,然后说明用途,并粘贴进入以下代码

class $className$(list: MutableList<$T$>) : BaseLtAdapterOneType<$T$>(list, R.layout.$next$) {
    override fun setData(v: BaseLtViewHolder.ViewFind, b: $T$, i: Int, h: BaseLtViewHolder) {
        $code$
    }
}

按照下面图示勾上Kotlin

然后点击右边的Edit variables按钮,设置成如下图的样子

然后新建一个Kotlin File,在空白处输入badapter,就会自动生成实现类,填入泛型和layoutId后就可以开心的用了

结语

emmm,好了,封装结束,小伙伴们可以直接复制最后一种封装方式,然后快乐的用RecyclerView编码了

当然,如果用的是别人封装过的adapter,也可以使用该方式进行二次封装,可以书写更方便

RecyclerView.Adapter还能在简化或优化性能吗?其实还有存货,不过等下次有时间了在写吧 \滑稽

发布了42 篇原创文章 · 获赞 192 · 访问量 5万+

猜你喜欢

转载自blog.csdn.net/qq_33505109/article/details/102916599