当 Adapter 遇上 Kotlin DSL,无比简单的调用方式
早在去年的时候我就提到过使用工厂的方式获取 Adapter 而不是为每个 Adapter 定义一个类文件。这样的好处是,对于不是那么复杂的 Adapter 可以节省大量的代码,提升开发效率和解放双手,同时更好的支持多类型布局效果。
1、Kotlin DSL 和 Adapter 工厂方法
可以把 Kotlin DSL 当作构建者使用。这里有一篇不错的文章,想了解的可以阅读下,
https://www.ximedes.com/2020-04-21/kotlin-dsl-tutorial/
Kotlin DSL 是拓展函数的延申,比如我们常用的 with 等函数就是函数的拓展,
1 2 3 4 5 6
| public inline fun <T, R> with(receiver: T, block: T.() -> R): R { contract { callsInPlace(block, InvocationKind.EXACTLY_ONCE) } return receiver.block() }
|
这里是泛型 T 的拓展。这里的 T 可以类比到 Java 构建者模式中的 Builder,通过方法接收外部参数之后调用 build()
方法创建一个最终的对象即可。
对于 Adapter 工厂方法,之前我是通过如下方式使用的,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| fun <T> getAdapter( @LayoutRes itemLayout:Int, converter: (helper: BaseViewHolder, item: T) -> Unit, data: List<T> ): Adapter<T> = Adapter(itemLayout, converter, data)
class Adapter<T>( @LayoutRes private val layout: Int, private val converter: (helper: BaseViewHolder, item: T) -> Unit, val list: List<T> ): BaseQuickAdapter<T, BaseViewHolder>(layout, list) { override fun convert(helper: BaseViewHolder, item: T) { converter(helper, item) } }
|
也就是每次想要得到 Adapter 的时候只要调用 getAdapter()
方法即可。这种封装方式比较简陋,支持的功能有限。后来慢慢采用了 Kotlin DSL 之后,我封装了 Kotlin DSL 风格的工厂方法。采用 Kotlin DSL 风格之后更加优雅和方便快捷,同时更好的支持多类型布局效果。
2、使用
2.1 引入依赖
首先,该项目依赖于 BRVAH,所以,你需要引入该库之后菜可以使用。BRVAH 可以说是目前开源的最好用的 Adapter,我们没必要再另起炉灶自己再造轮子。这个框架设计最好地方在于通过 SpareArray 收集了 ViewHolder 控件,从而避免了自定义 ViewHolder,这是我们框架设计的基础思想。
该项目已经上传到了 MavenCentral,你需要先在项目中引入该仓库,
1 2 3 4 5
| allprojects { repositories { mavenCentral() } }
|
然后在项目中添加如下依赖,
1
| implementation "com.github.Shouheng88:xadapter:${latest_version}"
|
2.2 使用 Adapter 工厂方法
使用 xAdapter 之后,当你需要定义一个 Adapter 的时候,你无需单独创建一个类文件,只需要通过 createAdapter()
方法获取一个 Adapter,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| adapter = createAdapter { withType(Item::class.java, R.layout.item_eyepetizer_home) { onBind { helper, item -> helper.setText(R.id.tv_title, item.data.title) helper.setText(R.id.tv_sub_title, item.data.author?.name + " | " + item.data.category) helper.loadCover(requireContext(), R.id.iv_cover, item.data.cover?.homepage, R.drawable.recommend_summary_card_bg_unlike) helper.loadRoundImage(requireContext(), R.id.iv_author, item.data.author?.icon, R.mipmap.eyepetizer, 20f.dp2px()) } onItemClick { _, _, position -> adapter?.getItem(position)?.let { toast("Clicked item: " + it.data.title) } } } }
|
在这种新的调用方式中,你需要通过 withType()
方法指定数据类型及其对应的布局文件,然后在 onBind()
方法中即可实现数据到 ViewHolder 的绑定操作。这里的 onBind()
方法的使用与 BRVAH 中的 convert()
方法使用一致,可以通过阅读该库了解如何使用。总之,xAapter 在 BRVAH 的基础上做了二次封装,可以说,比简单更简单。
xAdapter 支持为每个 ViewHolder 绑定点击和长按事件,同时也支持为 ViewHolder 上的某个单独的 View 添加点击和长按事件。使用方式如上所示,只需要添加 onItemClick()
方法并实现自己的逻辑即可。其他的点击事件可以参考项目的示例代码。
效果,

2.3 使用多类型 Adapter
多类型 Adapter 的使用方式非常简单,类似于上面的调用方式,只需要在 createAdapter()
内再添加一个 withType()
方法即可。下面是一个写起来可能相当复杂的 Adapter,但是采用了 xAdpater 的调用方式之后,一切变得非常简单,
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| private fun createAdapter() { adapter = createAdapter { withType(MultiTypeDataGridStyle::class.java, R.layout.item_list) { onBind { helper, item -> val rv = helper.getView<RecyclerView>(R.id.rv) rv.layoutManager = GridLayoutManager(context, 3) val adapter = createSubAdapter(R.layout.item_home_page_data_module_1, 1) rv.adapter = adapter adapter.setNewData(item.items) } } withType(MultiTypeDataListStyle1::class.java, R.layout.item_home_page_data_module_2) { onBind { helper, item -> converter.invoke(helper, item) } onItemClick { _, _, position -> (adapter?.getItem(position) as? MultiTypeDataListStyle1)?.let { toast("Clicked style[2] item: " + it.item.data.title) } } } withType(MultiTypeDataListStyle2::class.java, R.layout.item_list) { onBind { helper, item -> val rv = helper.getView<RecyclerView>(R.id.rv) rv.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false) val adapter = createSubAdapter(R.layout.item_home_page_data_module_4, 3) rv.adapter = adapter adapter.setNewData(item.items) } } withType(MultiTypeDataListStyle3::class.java, R.layout.item_home_page_data_module_3) { onBind { helper, item -> converter.invoke(helper, item) } onItemClick { _, _, position -> (adapter?.getItem(position) as? MultiTypeDataListStyle3)?.let { toast("Clicked style[4] item: " + it.item.data.title) } } } } }
|
xAdapter 对多类型布局方式的支持是在 BRVAH 之上进行的改造,在这种封装方式中,数据类无需实现任何类和接口。Adpater 内部通过 Class 区分各个 ViewHolder.
效果,

总结
相对于为各种类型的数据定义 Adapter 的使用方式,以上封装方式的优势是:
- 借助 BRVAH 的优势,封装了大量的方法,进一步简化了 Adapter 的使用;
- 通过工厂和 DSL 封装,简化了调用 Adapter 的方式,你无需为数据类型定义 Adapter 文件,减少了项目中需要维护的代码和类文件数量;
- 通过以上封装,使用 Adapter 更加简洁,节省了大量的代码,提升开发效率和解放双手;
- 自由地在单一类型布局和多类型布局之间进行切换,但是少了没必要的工厂方法。
当有更加简洁的使用方式的时候,继续采用复杂的调用方式无异于抱残守缺,对于程序员而言,做这种重复而没有太大价值的工作,付出再多的汗水都不值得同情。以上是部分功能和代码的展示,可以通过阅读源码了解更多。后续我参考其他优秀的库的设计思想,支持更多 Adapter 特性的封装来实现快速调用。
项目已开源,感兴趣的可以直接阅读项目源码,源码地址:https://github.com/Shouheng88/xAdapter