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

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • 详解分别用Kotlin和java写RecyclerView的示例

    本文介绍了分别用Kotlin和java写RecyclerView的示例,分享给大家,具体如下: java:跟一般的写法一样,增加了按钮响应 MainActivity: public class MainActivity extends AppCompatActivity implements RecyclerAdapter.OnItemClickListener{ private RecyclerView mRecyclerView; private RecyclerView.LayoutMan

  • Kotlin编写Android适配器Adapter

    说好今天要写一个使用Kotlin写Adapter的列子,我想了半天也没有组织好语言,直接上代码吧,有一定Android基础的小伙伴肯定是能看的懂的 package com.example.administrator.kotlintest import android.content.Context import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import and

  • Kotlin封装RecyclerView Adapter实例教程

    前言 Kotlin越来越流行,在Google的推动下发展的很迅猛,现在的项目大多使用上了Kotlin,其简练的语法糖确实能减少不少代码. Adapter的封装GitHub上有很多了,但大多数封装的太好了,是的,使用太简单了,使用简单.封装力度大就导致灵活性和代码复杂性上升,谁用谁知道,当然也有封装简单的. 这里我借助Kotlin的简单语法再次操刀封装了一下. 先看下使用 单类型的使用 val adapter=recyclerView.setUp(users, R.layout.item_layo

  • 自定义View之kotlin绘制折线图实例教程

    什么是Kotlin Kotlin,它是JetBrains开发的基于JVM的面向对象的语言.2017年的时候被Google推荐Android的官方语言,同时Android studio 3.0正式支持这门语言,在这个编译器上创建一个Kotlin项目,非常方便,甚至可以Java转为Kotlin. 引言 早上看到有个童鞋在群里面发牢骚,说这个自定义view怎么画,不太会,ok,正好我也没事,那我就花两个小时帮你搞定他吧,先看下他要的效果; 再来看下我实现的效果 实现过程 主要分为x轴和y轴,由效果图可

  • Android中对RecyclerView Adapter封装解析

    前言 关于adapter的封装,网上有很多开源库,开发的时候可以直接拿来用,省了很多事. 最近闲来无事,想着自己动手封装一个adapter. 问题 1.通常我们封装的时候,可以简化到这一步: BaseRecyclerViewAdapter adapter = new BaseRecyclerViewAdapter() { private static final int TYPE_FIR = 1; private static final int TYPE_SEC = 2; private st

  • Vue.js组件使用开发实例教程

    组件 组件可以扩展HTML元素,封装可重用的代码,在较高的层面上,组件是自定义元素,vue.js的编译器为它添加特殊功能,在有些情况下,组件也可以是原生HTML元素的形式,以is特性扩展. Vue.js的组件可以理解为预先定义好了行为的ViewModel类.一个组件可以预定义很多选项,但最核心的是以下几个: 模板(template):模板声明了数据和最终展现给用户的DOM之间的映射关系. 初始数据(data):一个组件的初始数据状态.对于可复用的组件来说,这通常是私有的状态. 接受的外部参数(p

  • Yii2 rbac权限控制之菜单menu实例教程

    在上篇文章给大家介绍了yii2搭建完美后台并实现rbac权限控制实例教程中完美实现了yii2的后台搭建和rbac权限控制,如果你还没有实现,请先看上文再回来参考本文,因为本文是在上文的基础上进行完善和补充. 部分小伙们纷纷反映,最后菜单menu怎么控制权限呀,看不懂,搞不定,而且你那貌似没搞完,瞎忽悠!确实没那么全,今天看我们如何实现菜单完美权限化.先罗列下主要讲的内容,不需要的没必要看下去,只为分享给有需要的人. 利用menu表添加菜单 左侧菜单结果adminlte完美呈现 菜单前面自定义ic

  • Android RecyclerView 数据绑定实例代码

    前言 在上一个项目里有很多很多很多很多的RecyclerView,然后我需要写很多很多很多很多的Adapter和Viewholder--多倒没问题,但是里面有很多重复的代码这就不能忍了!每一个Adapter和ViewHolder其实做的事情非常的像:视图绑定,数据绑定,点击事件分发.还有啥?既然它们做的事情都一样,为啥我们还要傻傻的继续写着重复的代码? 正文 BaseAdapter 通常我们要创建一个RecyclerView.Adapter是怎么做的? 接收一个数据列表 重写getItemCou

  • Android中封装RecyclerView实现添加头部和底部示例代码

    前言 我们大家都知道ListView具有添加头部和添加底部的方法,但是RecyclerView并没有这样子的方法.所以RecyclerView是不能添加底部和头部的,但是能不能仿造ListView来实现RecyclerView添加头部和底部呢?答案当然是可行的.本文就来给大家介绍了关于Android封装RecyclerView添加头部和底部的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. 首先看下实现的效果: 代码如下: public class WrapMyRecy

  • VUE2.0+Element-UI+Echarts封装的组件实例

    本文用Vue2.0+elementUI的panel组件和table组件+echarts的柱状图和折线图实现对结果的展示,实现了表格和图之间的切换和图和表之间的转置. -html <div class="resultDiv"> <div id="panels"> <el-collapse> <el-collapse-item v-for="item in indicators"> <templa

  • Android 使用Kotlin自定义View的方法教程

    前言 随着google宣布kotlin作为官方开发语言,在Android中使用kotlin的趋势也越来越明显,最近被kotlin的文章轰炸了,所以决定上手试一下,试过之后,感觉靠它灵简直有魔性.特别是一句话写出一个复杂的循环的时候,简直被惊呆.而且使用AS,Java代码可以直接转成Kotlin. 效果图如下: 首先是这次自定义View的效果图,是一张饼图.如果是用java写的话也就几十行,觉得换成Kotlin的话可能会更少. 示例代码 主要的功能是可以任设定数据的个数,我这里是4个数据,可以任意

  • 利用kotlin实现一个饼图实例代码

    前言 饼图是许多人最熟悉的图表类型,也是使用频率最高的图表类型之一,本文主要给大家介绍了关于利用kotlin实现饼图的相关内容,分享出来供大家参考学习,代码不难,所以打算用kotlin来实现,增加熟练度,下面来一起看看吧. 先看看做的是什么 看完图,我们来整理下思路 饼图居中,每块区域都是一个扇形,需要canvas.drawArc根据角度来绘制 需要path.arcTo定位到扇形弧度的一半来绘制折线的起点 通过canvas.drawPath绘制折线,折线的长度根据饼图大小来设置比例 通过canv

随机推荐