Kotlin封装RecyclerView Adapter实例教程
作者:蓦然地执着
前言
Kotlin越来越流行,在Google的推动下发展的很迅猛,现在的项目大多使用上了Kotlin,其简练的语法糖确实能减少不少代码。
Adapter的封装GitHub上有很多了,但大多数封装的太好了,是的,使用太简单了,使用简单、封装力度大就导致灵活性和代码复杂性上升,谁用谁知道,当然也有封装简单的。
这里我借助Kotlin的简单语法再次操刀封装了一下。
先看下使用
单类型的使用
val adapter=recyclerView.setUp(users, R.layout.item_layout, { holder, item -> var binding = DataBindingUtil.getBinding<ItemLayoutBinding>(holder.itemView) binding.nameText.text = item.name ... })
多类型的使用
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 ... }, { }) ))
使用就是如此简单,再来看下代码是不是过度封装
Adapter的基类
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 } } }
子类的实现和RecyclerView的扩展
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,所有核心代码,没有了,也不打算发布rar,要用的直接clone下来引入项目,这是最好的方式,因为不复杂,要改随时可以改。
看上面的多类型的使用,可以发现它是支持普通Layout和DataBinding Layout的,这也是本库的一个特色,不需要多余的处理。
1.普通的Layout 这样处理
ListItem(R.layout.item_layout2, { holder, item -> val nameText: TextView = holder.getView(R.id.nameText) nameText.text = item.name }
通过Holder来操作View,里面有做缓存的。
DataBinding Layout ListItem(R.layout.item_layout, { holder, item -> var binding = DataBindingUtil.getBinding<ItemLayoutBinding>(holder.itemView) binding.nameText.text = item.name }
是不是只要自己知道是哪中Layout,对应处理就可以了,Holder处理方式也是可以处理DataBinding Layout的,要知晓。
这里提下,可能有人会问干嘛不直接用Kotlin的Layout View 查找方法???
那样代码看起来是简单,但是现在的Studio 对这个的支持不是很好,经常报红,程序员看到红会烦躁啊!!如果还是喜欢的话实现也很简单,改成View的扩展返回就可以了,可以自己动手试下哦。
因为这里只是对不变的部分进行了封装,没有很多华丽丽的添加头部、脚部啥的功能,点击事件倒是内置了一种,当然点击事件还可以用ItemTouchHelper实现,都是可以的。
这样每次就不用写一大串的Adaper了,是不是可以开心地泡壶茶,吹口气了。
别的库都可以Item复用的,你的可以吗?
嗯嗯、、?可以的
比如
val item: (AbstractAdapter.Holder, User) -> Unit = { holder, user -> }
再比如
ListItem(R.layout.item_layout, { holder, item -> var binding = DataBindingUtil.getBinding<ItemLayoutBinding>(holder.itemView) }, {//点击事件 Snackbar.make(window.decorView, it.name, Snackbar.LENGTH_SHORT).show() })
是不是一样可以的 只要定义到一个地方 然后设置进去就可以了,复用也是难不倒它的。只能说Kotlin语法大法好。
好了,这个库就介绍到这里了,谢谢大家。
代码地址
灵感来自下面这位大神,但是我基本重写了
https://github.com/armcha/Kadapter
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对脚本之家的支持。