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

15 篇文章 0 订阅
1 篇文章 0 订阅

前言

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(this@BaseLtViewHolder.view) }

    /**
     * 使用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
        }
    }

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

ps:后来发现可以使用LayoutContainer,性能可以更好,可以参考GitHub - ltttttttttttt/ltviews: Android custom View: Easy to use RecyclerView, image banner, general drop-down refresh view里的ltviewsx里的BaseAdapterOneType和BaseLtViewHolder

利用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还能在简化或优化性能吗?其实还有存货,不过等下次有时间了在写吧 \滑稽

对Kotlin或KMP感兴趣的同学可以进Q群 101786950

如果这篇文章对您有帮助的话

可以扫码请我喝瓶饮料或咖啡(如果对什么比较感兴趣可以在备注里写出来)

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
RecyclerView.Adapter 是一个用于管理 RecyclerView 中数据和视图的基类。它有三个主要的方法:onCreateViewHolder、onBindViewHolder 和 getItemCount。 onCreateViewHolder 方法用于创建 ViewHolder 对象,ViewHolder 对象用于保存 RecyclerView 中的视图。onBindViewHolder 方法用于将数据绑定到 ViewHolder 中的视图上。getItemCount 方法用于返回 RecyclerView 中的数据项数量。 在实现 RecyclerView.Adapter 时,我们要重写这三个方法,并根据实际求进相应的处理。此外,我们还可以添加一些其他的方法,例如添加、删除、更新数据等。 下面是一个简单的 RecyclerView.Adapter 实现示例: ``` public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> { private List<String> mData; public MyAdapter(List<String> data) { mData = data; } @Override public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false); ViewHolder holder = new ViewHolder(view); return holder; } @Override public void onBindViewHolder(ViewHolder holder, int position) { String data = mData.get(position); holder.mTextView.setText(data); } @Override public int getItemCount() { return mData.size(); } public static class ViewHolder extends RecyclerView.ViewHolder { public TextView mTextView; public ViewHolder(View itemView) { super(itemView); mTextView = (TextView) itemView.findViewById(R.id.text_view); } } } ``` 在这个示例中,我们创建了一个 MyAdapter 类,它继承自 RecyclerView.Adapter。在构造函数中,我们传入了一个数据列表 mData。在 onCreateViewHolder 方法中,我们使用 LayoutInflater 创建了一个视图,并将其封装在 ViewHolder 中返回。在 onBindViewHolder 方法中,我们将数据绑定到 ViewHolder 中的视图上。在 getItemCount 方法中,我们返回 mData 的大小。 这是一个简单的 RecyclerView.Adapter 实现示例,实际应用中可能要更复杂的处理逻辑。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值