-
-
Notifications
You must be signed in to change notification settings - Fork 59
谈谈设计思想
回顾一下RecycleView
工作方式:
首先, RecycleView
是一个ViewGroup
, 是一个高级扩展, 和具有缓存功能的超级View容器
, 不言而喻.
为了让RecycleView
能够显示视图,需要指定一个LayoutManager
用于控制View
的测量和布局. 其次还需要一个Adapter
用于提供数据和View
操作.
简单2个配置,就可以使RecycleView
工作.
DslAdapter
就是直接继承Adapter
类的, 主要用于管理数据DslAdapterItem
(以下简称Item
).
现在市场上的很多Adapter
库, 都是简单的操作Data
数据Bean.
在我的设计里, 我把数据Item
超级化了
, 使得Item
不仅仅是数据Data
bean的载体, 同时还是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
的itemLayoutId
xml布局资源, 这样就可以自动根据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
这是嘛玩意? 为何要有这玩意? 有鸟用? 懵逼三连.
不慌, 听说吹一下.
在介绍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
集合中.
这个类是最简单的
, 也是最重要的
也是.
Q:为什么是最简单的
?
A:因为这个类, 基本上是0逻辑代码.
Q:那为什么又是最重要的
?
A:因为这个类, 包含了一切您有效的数据和配置. 库中的所有效果实现, 都需要在此类中进行相关配置.
所以, 完全可以基于DslAdapterItem
打造出您的超级Item
(比如:AppItem
)
魅力无极限, 期待您的表现
比如:
/**
* 当前[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
DslDataFilter
DslAdapterItem
这个类就是RecycleView
中的Adapter
, 在我的设计中就是数据容器, 用来存储界面上需要显示的数据.
这个类就是从数据容器DslAdapter
中, 拿出需要在界面上真正展示数据的操作类.
这个类就是在界面上展示视图, 通过DslViewHoler
类操作视图.
当您迷失了方向:
这个库, 也许能给你启发: DslItem