Kotlin封裝RecyclerView Adapter實(shí)例教程
前言
Kotlin越來(lái)越流行,在Google的推動(dòng)下發(fā)展的很迅猛,現(xiàn)在的項(xiàng)目大多使用上了Kotlin,其簡(jiǎn)練的語(yǔ)法糖確實(shí)能減少不少代碼。
Adapter的封裝GitHub上有很多了,但大多數(shù)封裝的太好了,是的,使用太簡(jiǎn)單了,使用簡(jiǎn)單、封裝力度大就導(dǎo)致靈活性和代碼復(fù)雜性上升,誰(shuí)用誰(shuí)知道,當(dāng)然也有封裝簡(jiǎn)單的。
這里我借助Kotlin的簡(jiǎn)單語(yǔ)法再次操刀封裝了一下。
先看下使用
單類(lèi)型的使用
val adapter=recyclerView.setUp(users, R.layout.item_layout, { holder, item ->
var binding = DataBindingUtil.getBinding<ItemLayoutBinding>(holder.itemView)
binding.nameText.text = item.name
...
})
多類(lèi)型的使用
recyclerView.setUP(users,
listItems = *arrayOf(
ListItem(R.layout.item_layout, { holder, item ->
var binding = DataBindingUtil.getBinding<ItemLayoutBinding>(holder.itemView)
binding?.nameText?.text = item.name
...
}, {
Snackbar.make(window.decorView, it.name, Snackbar.LENGTH_SHORT).show()
}),
ListItem(R.layout.item_layout2, { holder, item ->
val nameText: TextView = holder.getView(R.id.nameText)
nameText.text = item.name
...
}, {
})
))
使用就是如此簡(jiǎn)單,再來(lái)看下代碼是不是過(guò)度封裝
Adapter的基類(lèi)
abstract class AbstractAdapter<ITEM> constructor(protected var itemList: List<ITEM>)
: RecyclerView.Adapter<AbstractAdapter.Holder>() {
override fun getItemCount() = itemList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): Holder {
val view = createItemView(parent, viewType)
val viewHolder = Holder(view)
val itemView = viewHolder.itemView
itemView.setOnClickListener {
val adapterPosition = viewHolder.adapterPosition
if (adapterPosition != RecyclerView.NO_POSITION) {
onItemClick(itemView, adapterPosition)
}
}
return viewHolder
}
fun update(items: List<ITEM>) {
updateAdapterWithDiffResult(calculateDiff(items))
}
private fun updateAdapterWithDiffResult(result: DiffUtil.DiffResult) {
result.dispatchUpdatesTo(this)
}
private fun calculateDiff(newItems: List<ITEM>) =
DiffUtil.calculateDiff(DiffUtilCallback(itemList, newItems))
fun add(item: ITEM) {
itemList.toMutableList().add(item)
notifyItemInserted(itemList.size)
}
fun remove(position: Int) {
itemList.toMutableList().removeAt(position)
notifyItemRemoved(position)
}
final override fun onViewRecycled(holder: Holder) {
super.onViewRecycled(holder)
onViewRecycled(holder.itemView)
}
protected open fun onViewRecycled(itemView: View) {
}
protected open fun onItemClick(itemView: View, position: Int) {
}
protected abstract fun createItemView(parent: ViewGroup, viewType: Int): View
class Holder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val views = SparseArray<View>()
fun <T : View> getView(viewId: Int): T {
var view = views[viewId]
if (view == null) {
view = itemView.findViewById(viewId)
views.put(viewId, view)
}
return view as T
}
}
}
子類(lèi)的實(shí)現(xiàn)和RecyclerView的擴(kuò)展
class SingleAdapter<ITEM>(items: List<ITEM>,
private val layoutResId: Int,
private val bindHolder: (Holder, ITEM) -> Unit)
: AbstractAdapter<ITEM>(items) {
private var itemClick: (ITEM) -> Unit = {}
constructor(items: List<ITEM>,
layoutResId: Int,
bindHolder: (Holder, ITEM) -> Unit,
itemClick: (ITEM) -> Unit = {}) : this(items, layoutResId, bindHolder) {
this.itemClick = itemClick
}
override fun createItemView(parent: ViewGroup, viewType: Int): View {
var view = parent inflate layoutResId
if (view.tag?.toString()?.contains("layout/") == true) {
DataBindingUtil.bind<ViewDataBinding>(view)
}
return view
}
override fun onBindViewHolder(holder: Holder, position: Int) {
bindHolder(holder, itemList[position])
}
override fun onItemClick(itemView: View, position: Int) {
itemClick(itemList[position])
}
}
class MultiAdapter<ITEM : ListItemI>(private val items: List<ITEM>,
private val bindHolder: (Holder, ITEM) -> Unit)
: AbstractAdapter<ITEM>(items) {
private var itemClick: (ITEM) -> Unit = {}
private lateinit var listItems: Array<out ListItem<ITEM>>
constructor(items: List<ITEM>,
listItems: Array<out ListItem<ITEM>>,
bindHolder: (Holder, ITEM) -> Unit,
itemClick: (ITEM) -> Unit = {}) : this(items, bindHolder) {
this.itemClick = itemClick
this.listItems = listItems
}
override fun createItemView(parent: ViewGroup, viewType: Int): View {
var view = parent inflate getLayoutId(viewType)
if (view.tag?.toString()?.contains("layout/") == true) {
DataBindingUtil.bind<ViewDataBinding>(view)
}
return view
}
private fun getLayoutId(viewType: Int): Int {
var layoutId = -1
listItems.forEach {
if (it.layoutResId == viewType) {
layoutId = it.layoutResId
return@forEach
}
}
return layoutId
}
override fun getItemViewType(position: Int): Int {
return items[position].getType()
}
override fun onBindViewHolder(holder: Holder, position: Int) {
bindHolder(holder, itemList[position])
}
override fun onItemClick(itemView: View, position: Int) {
itemClick(itemList[position])
}
}
fun <ITEM> RecyclerView.setUp(items: List<ITEM>,
layoutResId: Int,
bindHolder: (AbstractAdapter.Holder, ITEM) -> Unit,
itemClick: (ITEM) -> Unit = {},
manager: RecyclerView.LayoutManager = LinearLayoutManager(this.context)): AbstractAdapter<ITEM> {
val singleAdapter by lazy {
SingleAdapter(items, layoutResId, { holder, item ->
bindHolder(holder, item)
}, {
itemClick(it)
})
}
layoutManager = manager
adapter = singleAdapter
return singleAdapter
}
fun <ITEM : ListItemI> RecyclerView.setUP(items: List<ITEM>,
manager: RecyclerView.LayoutManager = LinearLayoutManager(this.context),
vararg listItems: ListItem<ITEM>): AbstractAdapter<ITEM> {
val multiAdapter by lazy {
MultiAdapter(items, listItems, { holder, item ->
var listItem: ListItem<ITEM>? = getListItem(listItems, item)
listItem?.bindHolder?.invoke(holder, item)
}, { item ->
var listItem: ListItem<ITEM>? = getListItem(listItems, item)
listItem?.itemClick?.invoke(item)
})
}
layoutManager = manager
adapter = multiAdapter
return multiAdapter
}
private fun <ITEM : ListItemI> getListItem(listItems: Array<out ListItem<ITEM>>, item: ITEM): ListItem<ITEM>? {
var listItem: ListItem<ITEM>? = null
listItems.forEach {
if (it.layoutResId == item.getType()) {
listItem = it
return@forEach
}
}
return listItem
}
class ListItem<ITEM>(val layoutResId: Int,
val bindHolder: (holder: AbstractAdapter.Holder, item: ITEM) -> Unit,
val itemClick: (item: ITEM) -> Unit = {})
interface ListItemI {
fun getType(): Int
}
ok,所有核心代碼,沒(méi)有了,也不打算發(fā)布rar,要用的直接clone下來(lái)引入項(xiàng)目,這是最好的方式,因?yàn)椴粡?fù)雜,要改隨時(shí)可以改。
看上面的多類(lèi)型的使用,可以發(fā)現(xiàn)它是支持普通Layout和DataBinding Layout的,這也是本庫(kù)的一個(gè)特色,不需要多余的處理。
1.普通的Layout 這樣處理
ListItem(R.layout.item_layout2, { holder, item ->
val nameText: TextView = holder.getView(R.id.nameText)
nameText.text = item.name
}
通過(guò)Holder來(lái)操作View,里面有做緩存的。
DataBinding Layout
ListItem(R.layout.item_layout, { holder, item ->
var binding = DataBindingUtil.getBinding<ItemLayoutBinding>(holder.itemView)
binding.nameText.text = item.name
}
是不是只要自己知道是哪中Layout,對(duì)應(yīng)處理就可以了,Holder處理方式也是可以處理DataBinding Layout的,要知曉。
這里提下,可能有人會(huì)問(wèn)干嘛不直接用Kotlin的Layout View 查找方法???
那樣代碼看起來(lái)是簡(jiǎn)單,但是現(xiàn)在的Studio 對(duì)這個(gè)的支持不是很好,經(jīng)常報(bào)紅,程序員看到紅會(huì)煩躁?。。∪绻€是喜歡的話(huà)實(shí)現(xiàn)也很簡(jiǎn)單,改成View的擴(kuò)展返回就可以了,可以自己動(dòng)手試下哦。
因?yàn)檫@里只是對(duì)不變的部分進(jìn)行了封裝,沒(méi)有很多華麗麗的添加頭部、腳部啥的功能,點(diǎn)擊事件倒是內(nèi)置了一種,當(dāng)然點(diǎn)擊事件還可以用ItemTouchHelper實(shí)現(xiàn),都是可以的。
這樣每次就不用寫(xiě)一大串的Adaper了,是不是可以開(kāi)心地泡壺茶,吹口氣了。
別的庫(kù)都可以Item復(fù)用的,你的可以嗎?
嗯嗯、、?可以的
比如
val item: (AbstractAdapter.Holder, User) -> Unit = { holder, user ->
}
再比如
ListItem(R.layout.item_layout, { holder, item ->
var binding = DataBindingUtil.getBinding<ItemLayoutBinding>(holder.itemView)
}, {//點(diǎn)擊事件
Snackbar.make(window.decorView, it.name, Snackbar.LENGTH_SHORT).show()
})
是不是一樣可以的 只要定義到一個(gè)地方 然后設(shè)置進(jìn)去就可以了,復(fù)用也是難不倒它的。只能說(shuō)Kotlin語(yǔ)法大法好。
好了,這個(gè)庫(kù)就介紹到這里了,謝謝大家。
代碼地址
靈感來(lái)自下面這位大神,但是我基本重寫(xiě)了
https://github.com/armcha/Kadapter
總結(jié)
以上就是這篇文章的全部?jī)?nèi)容了,希望本文的內(nèi)容對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,如果有疑問(wèn)大家可以留言交流,謝謝大家對(duì)腳本之家的支持。
相關(guān)文章
詳解Android中PopupWindow在7.0后適配的解決
本篇文章主要介紹了詳解Android中PopupWindow在7.0后適配的解決,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-05-05
Android中簡(jiǎn)單的電話(huà)管理與短信管理App編寫(xiě)實(shí)例
這篇文章主要介紹了Android中簡(jiǎn)單的電話(huà)管理與短信管理App編寫(xiě)實(shí)例,包括監(jiān)聽(tīng)電話(huà)的呼叫狀態(tài)以及短信群發(fā)聯(lián)系人選擇等基本功能的實(shí)現(xiàn),代碼突出要點(diǎn),需要的朋友可以參考下2016-04-04
一分鐘快速定位Android啟動(dòng)耗時(shí)問(wèn)題
做開(kāi)發(fā)除了實(shí)現(xiàn)功能,還要注重優(yōu)化,性能優(yōu)化包括的東西還是非常多的,下面這篇文章主要給大家介紹了關(guān)于如何通過(guò)一分鐘快速定位Android啟動(dòng)耗時(shí)問(wèn)題的相關(guān)資料,需要的朋友可以參考下2021-07-07
Android Notification的多種用法總結(jié)
這篇文章主要介紹了Android Notification的多種用法總結(jié)的相關(guān)資料,需要的朋友可以參考下2017-06-06
Android Studio中CodeStyle模板的配置方式
這篇文章主要介紹了Android Studio中CodeStyle模板的配置方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-03-03
Android Webview與ScrollView的滾動(dòng)兼容及留白處理的方法
本篇文章主要介紹了Android Webview與ScrollView的滾動(dòng)兼容及留白處理的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-11-11
Material Design系列之自定義Behavior支持所有View
這篇文章主要為大家詳細(xì)介紹了Material Design系列之自定義Behavior支持所有View,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2016-09-09
Android WebView輸入框被檔問(wèn)題升級(jí)解析
這篇文章主要為大家介紹了Android WebView輸入框被檔問(wèn)題升級(jí)解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-02-02

