Skip to content

谈谈设计思想

angcyo edited this page Jul 6, 2020 · 5 revisions

2020-07-04

回顾

回顾一下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.

知道了DslAdapterDslAdapterItem的关系, 下面重点讲一下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也就是ItemitemLayoutIdxml布局资源, 这样就可以自动根据xml布局资源的不同实现多类型的Item, 而不必维护positionitemType之间的关系.

onCreateViewHolder仅仅创建了一个DslViewHolder对象, 对应View的操作, 全部交给DslViewHolder即可. 不必继承编写各种特定的ViewHolder意义不大.

onBindViewHolder这个方法只是将bind操作转发给了对应的Item, 并无特殊之处.

现在重点看一下getItemDatagetValidFilterDataList:

/**获取有效过滤后的数据集合*/
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.adapterItemsdslDataFilter.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添加到dslAdapteradapterItems集合中,

然后dslAdapter通过DslDataFilter的方法updateFilterItemDepend, 将dslAdapter中的集合数据adapterItems一顿操作到dslDataFilter中的filterDataList数据集合.

然后dslDataFilter通过DiffUtil将差异化的结果刷新到RecycleView界面.

这样整个流程就完成. have fun!

DslAdapter这个DslAdapterItem容器有三个数据集合可以存放Item, 分别是headerItems dataItems footerItems, 区别不大, 想用哪个就用哪个,无特殊需要处理的地方. 因为最终这3个集合的数据都会被汇总到adapterItems集合中.

介绍DslAdapterItem

这个类是最简单的, 也是最重要的也是.

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中几个重要的概念

DslAdapter DslDataFilter DslAdapterItem

DslAdapter

这个类就是RecycleView中的Adapter, 在我的设计中就是数据容器, 用来存储界面上需要显示的数据.

DslDataFilter

这个类就是从数据容器DslAdapter中, 拿出需要在界面上真正展示数据的操作类.

DslAdapterItem

这个类就是在界面上展示视图, 通过DslViewHoler类操作视图.

其他

当您迷失了方向:

这个库, 也许能给你启发: DslItem