# 设计思想
# 回顾
回顾一下RecycleView工作方式:
首先, RecycleView是一个ViewGroup, 是一个高级扩展, 和具有缓存功能的超级View容器, 不言而喻.
为了让RecycleView能够显示视图,需要指定一个LayoutManager用于控制View的测量和布局. 其次还需要一个Adapter用于提供数据和View操作.
简单2个配置,就可以使RecycleView工作.
# 介绍DslAdapter
DslAdapter就是直接继承Adapter类的, 主要用于管理数据DslAdapterItem(以下简称Item).
现在市场上的很多Adapter库, 都是简单的操作Data数据Bean.
在我的设计里, 我把数据Item 超级化了, 使得Item不仅仅是数据Databean的载体, 同时还是View的控制类, 同时还是高级功能的配置类. Item只负责功能的配置, 而不具备功能的实现逻辑. 功能的实现, 需要对应的组件配合才能正常工作.
所以, 即使是库的使用者, 也可以通过Item的配置, 实现各种强大的自定义功能. 而不仅仅局限于库的默认实现.
因为Item是一个超级配置类, 所以我在库中写了API, 将Item支持在普通的ViewGroup中加载, 但是目前只支持部分功能的实现, 特殊功能还需要RecycleView的支持.
通过这个例子, 我喜欢使用者能明白Item的作用, 下面还会详细介绍Item.
知道了DslAdapter和DslAdapterItem的关系, 下面重点讲一下DslAdapter:
和普通的Adapter一样, 一些必要的方法都是需要重写的:
open class DslAdapter : RecyclerView.Adapter<DslViewHolder>(){
override fun getItemViewType(position: Int): Int {
return getItemData(position)?.itemLayoutId ?: 0
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): DslViewHolder {
//viewType, 就是布局的 Id, 这是设计核心原则.
val dslViewHolder: DslViewHolder
val itemView: View = LayoutInflater.from(parent.context).inflate(viewType, parent, false)
dslViewHolder = DslViewHolder(itemView)
return dslViewHolder
}
override fun getItemCount(): Int {
return getValidFilterDataList().size
}
override fun onBindViewHolder(
holder: DslViewHolder,
position: Int,
payloads: List<Any>
) {
super.onBindViewHolder(holder, position, payloads)
val dslItem = getItemData(position)
dslItem?.itemDslAdapter = this
dslItem?.itemBind?.invoke(holder, position, dslItem, payloads)
}
}
可以看到, 核心的几个方法特别简单. 这里有个小技巧, 也是重点核心.
这里getItemViewType返回的是getItemData(position)?.itemLayoutId也就是Item的itemLayoutIdxml布局资源, 这样就可以自动根据xml布局资源的不同实现多类型的Item, 而不必维护position和itemType之间的关系.
onCreateViewHolder仅仅创建了一个DslViewHolder对象, 对应View的操作, 全部交给DslViewHolder即可. 不必继承编写各种特定的ViewHolder意义不大.
onBindViewHolder这个方法只是将bind操作转发给了对应的Item, 并无特殊之处.
现在重点看一下getItemData和getValidFilterDataList:
/**获取有效过滤后的数据集合*/
fun getValidFilterDataList(): List<DslAdapterItem> {
return dslDataFilter?.filterDataList ?: adapterItems
}
fun getItemData(position: Int, useFilterList: Boolean = true): DslAdapterItem? {
val list = getDataList(useFilterList)
return if (position in list.indices) {
list[position]
} else {
null
}
}
/**获取数据列表*/
fun getDataList(useFilterList: Boolean = true): List<DslAdapterItem> {
return if (useFilterList) getValidFilterDataList() else adapterItems
}
会发现最后调用了dslDataFilter?.filterDataList, 那这是啥?
这里就必须要再抛出一个核心类DslDataFilter.
var dslDataFilter: DslDataFilter? = null
# 介绍DslDataFilter
这是嘛玩意? 为何要有这玩意? 有鸟用? 懵逼三连.
不慌, 听说吹一下.
在介绍DslAdapter的时候, 已经说过了它是管理DslAdapterItem集合的.
但是在这里, 我要纠正一下这个管理: 其实DslAdapter就是DslAdapterItem的一个容器, 这个容器里面存放着很多DslAdapterItem, 但是这些Item并不是直接用于显示在RecycleView上的.
而是通过DslDataFilter进行层层筛选(比如:隐藏了某些Item,折叠了某些Item等), 最终最美丽的Item才会被RecycleView显示.
这样做的目的就是, 可以完全控制所有的Item, 完全可以让您的控制欲达到顶峰.
这就是DslDataFilter设计的目的.
那么filterDataList是什么? 怎么来的? 怎么更新的呢?
在DslDataFilter的源码中, 可以看到filterDataList的声明:
/**
* 过滤后的数据源, 缓存过滤后的数据源, 防止每次都计算.
*
* 当有原始数据源发生改变时, 需要调用 [updateFilterItems] 更新过滤后的数据源
* */
val filterDataList: MutableList<DslAdapterItem> = mutableListOf()
这就是最终RecycleView真实显示的Item数据集合. 这个和dslAdapter.adapterItems集合不一样哦.
dslAdapter.adapterItems和dslDataFilter.filterDataList的区别:
dslDataFilter.filterDataList的数据是dslAdapter.adapterItems子集.
dslAdapter.adapterItems是使用者添加的所有Item(可见和不可见的)
dslDataFilter.filterDataList是所有可见的Item(不包含不可见的)
其次dslDataFilter.filterDataList中的数据, 是在dslAdapter.adapterItems筛选出来的.
那怎么更新dslDataFilter.filterDataList数据呢?
dslDataFilter有个方法叫做updateFilterItemDepend.
open fun updateFilterItemDepend(params: FilterParams) {
...
}
这个方法就是用来更新filterDataList数据集合的, 最后通过android系统的DiffUtil工具androidx.recyclerview.widget.DiffUtil.DiffResult#dispatchUpdatesTo(androidx.recyclerview.widget.RecyclerView.Adapter)刷新界面.
在DslAdapter中可以使用updateItemDepend方法, 进行更新操作.
fun updateItemDepend(filterParams: FilterParams = defaultFilterParams!!) {
dslDataFilter?.let {
it.updateFilterItemDepend(filterParams)
if (filterParams == onceFilterParams) {
onceFilterParams = null
}
}
}
此方法内部就是调用updateFilterItemDepend实现的.
总结一下
使用者通过DslAdapter的各种操作(add insert + invoke等), 将DslAdapterItem添加到dslAdapter的adapterItems集合中,
然后dslAdapter通过DslDataFilter的方法updateFilterItemDepend, 将dslAdapter中的集合数据adapterItems一顿操作到dslDataFilter中的filterDataList数据集合.
然后dslDataFilter通过DiffUtil将差异化的结果刷新到RecycleView界面.
这样整个流程就完成. have fun!
DslAdapter这个DslAdapterItem容器有三个数据集合可以存放Item, 分别是headerItems dataItems footerItems, 区别不大, 想用哪个就用哪个,无特殊需要处理的地方. 因为最终这3个集合的数据都会被汇总到adapterItems集合中.
# 介绍DslAdapterItem
最没用的就是这个类, 最重要的也是这个类.
为什么是最没用的?
因为这个类, 基本上是0逻辑代码.
那为什么又是最重要的?
因为这个类, 包含了一切您有效的数据和配置. 库中的所有效果实现, 都需要在此类中进行相关配置.
比如:
/**
* 当前[DslAdapterItem]是否可以被拖拽.需要[DragCallbackHelper]的支持
* [itemIsGroupHead]
* [DragCallbackHelper.getMovementFlags]
* */
var itemDragEnable = true
/**
* 当前[DslAdapterItem]是否可以被侧滑删除.需要[DragCallbackHelper]的支持
* */
var itemSwipeEnable = true
/**
* 当前item, 是否是分组的头, 设置了分组, 默认会开启悬停
*
* 如果为true, 哪里折叠此分组是, 会 伪删除 这个分组头, 到下一个分组头 中间的 data
* */
var itemIsGroupHead = false
set(value) {
field = value
if (value) {
itemIsHover = true
itemDragEnable = false
itemSpanCount = -1
}
}
/**
* 当前分组是否[展开]
* */
var itemGroupExtend: Boolean by UpdateDependProperty(true)
/**是否需要隐藏item*/
var itemHidden: Boolean by UpdateDependProperty(false)
/**
* 是否需要悬停, 在使用了 [HoverItemDecoration] 时, 有效.
* [itemIsGroupHead]
* */
var itemIsHover: Boolean = itemIsGroupHead
全是配置参数, 功能的逻辑实现都在对应的组件中.
所以, 对于使用者来说. 网络请求的数据等, 直接继承DslAdapterItem自己声明一个任意类型的成员变量保存即可.
# 总结DslAdapter中几个重要的概念
DslAdapter DslDataFilter DslAdapterItem
# DslAdapter
这个类就是RecycleView中的Adapter, 在我的设计中就是数据容器, 用来存储界面上需要显示的数据.
# DslDataFilter
这个类就是从数据容器DslAdapter中, 拿出需要在界面上真正展示数据的操作类.
# DslAdapterItem
这个类就是在界面上展示视图, 通过DslViewHoler类操作视图.