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