前言
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還能在簡化或優化性能嗎?其實還有存貨,不過等下次有時間了在寫吧 \滑稽