-
-
Notifications
You must be signed in to change notification settings - Fork 59
数据刷新,更新和加载更多
DslAdapter
唯一能接受的数据类型就是DslAdapterItem
.
DslAdapterItem
是一个超级对象
(以下简称Item
), 通常开发者都需要继承此类, 实现界面组件.
Item
是组成界面的最小单位, 通过多个Item
最终组成简单/复杂的界面.
比如Demo
中的DslDemoItem
class DslDemoItem : DslAdapterItem() {
var itemText: CharSequence? = null
init {
itemLayoutId = R.layout.item_demo_list
}
override fun onItemBind(
itemHolder: DslViewHolder,
itemPosition: Int,
adapterItem: DslAdapterItem,
payloads: List<Any>
) {
super.onItemBind(itemHolder, itemPosition, adapterItem, payloads)
itemHolder.tv(R.id.text_view)?.text = itemText
}
}
几个关键点
- 继承自
DslAdapterItem
//必须 - 设置
itemLayoutId
//必须 - 重写
onItemBind
//可选. (因为还有很多同功能的API)
对于初学者, 会使用上面3步即可.
在上述的DslDemoItem
中, itemText
就是DslDemoItem
的数据.
当然, 您也可以声明多个任意命名
任意类型
的数据, 库对此毫无依赖.
如此声明之后, 如何在界面展示呢?
Item
需要在界面能看到, 那么Item
就需要关联到DslAdapter
, 然后DslAdapter
关联RecyclerView
, 这样就能在界面上展示了.
DslAdapter
提供了多种api
, 方便关联Item
, 但最终核心都是将Item
放到DslAdapter
内部的footerItems
headerItems
或者dataItems
成员变量中.
最后不要忘了, 调用dslAdapter.updateItemDepend
刷新界面哦.
这些个列表数据集合, 通过一个叫做DslDataFilter
过滤器, 摘取部分
或者全部
的Item
丢到RecyclerView
中展示.
Q: 为什么会出现
部分
数据丢到RecyclerView
中展示呢?
A: 这是因为
DslAdapter
是支持Item
隐藏/分组折叠的.被隐藏的Item
虽然依旧存在DslAdapter
中, 但是界面上却不存在.
关于 footerItems
headerItems
dataItems
其实理论上只需要一个dataItems
即可, 当初设计的时候, 为了大家方便管理, 才多加了几个数据集合.
footerItems
headerItems
dataItems
数据集合的数据, 在更新到界面RecycleView
之前, 都会被合并到一个叫做adapterItems
数据集合中, 不推荐直接操作adapterItems
, 当然如果您对库了如指掌, 也是可以直接使用此变量的.
无特殊说明的情况下, 库中
API
默认只操作dataItems
要将Item
关联到DslAdapter
提供了以下多种方法:
dslAdapter.addLastItem(item)
dslAdapter.addLastItem(list<item>)
dslAdapter.insertItem(0, item)
dslAdapter.resetItem(list<item>) //先clear, 后addAll
//移除操作
dslAdapter.removeItem(item)
dslAdapter.removeItem(list<item>)
//以上方法都会默认触发`updateItemDepend`
//如果需要, 也可以手动触发`updateItemDepend`
库中重写了一些操作符
, 所以你可以使用以下方式:
dslAdapter + DslAdapterItem().apply{}
dslAdapter - DslAdapterItem()
// 这是本人推荐的使用方法
dslAdapter.apply{
DslDemoItem()(){
...
}
}
库中提供了3个超级API,用于直接操作对应的数据源列表
:
changeDataItems{ dataItems->
//直接操作dataItems
}
changeHeaderItems{ headerItems->
//同上
}
changeFooterItems{ footerItems->
//同上
}
//以上方法都可以直接操作对应的数据源, 操作后并会更新界面
//以上方法都会默认触发`updateItemDepend`
如何更新已经Item
呢?
- 通过
Tag
查找对应Item
,并更新.
设置DslAdapterItem
的成员变量itemTag
, 然后通过dslAdapter.findItemByTag(xxx)
扩展方法, 找到对应的Item
, 最后调用item.updateAdapterItem
刷新界面.
//1.
dslAdapter.apply{
DslDemoItem()(){
itemTag = "xxx" //设置tag
}
}
//2.
dslAdapter.findItemByTag("xxx")?.apply {
if (this is DslDemoItem) {
itemText = "数据刷新:${System.currentTimeMillis()}"
updateAdapterItem()
}
}
- 如何在列表中更新
Item
呢?
不管在什么地方更新Item
, 核心都是1
中的思路, 拿到目标Item
更新对应的数据成员变量, 刷新界面. 完事收工.
只不过, 作为库的开发者. 要时刻为了使用者考虑, 减轻使用者的使用成本.
所以, 库提供了很多扩展方法, 方便操作. 但请注意上述的核心, 因为万变不离其宗. 我写的API, 也是围绕核心步骤编写的.
为了方便在列表中更新Item
, 我写了一个Config叫UpdateDataConfig
这个类的可配置项如下:
var updateDataList: List<Any>? = null
本次更新的数据集合列表, 通常会是网络请求返回的list<bean>
数据集合, 支持任意类型, 直接赋值塞过来即可.
var updatePage: Int = Page.FIRST_PAGE_IN
var pageSize: Int = Page.PAGE_SIZE
这2个参数, 是用来控制分页加载的. updatePage
用来控制当前数据是第几页的,pageSize
用来控制当前页请求的数据量(数据量过多时, DslAdapter
底部会出现加载更多
的Item
. 数据量不够时, DslAdapter
底部会出现我是有底线
的Item
).
/**
* 更新已有的item, 创建不存在的item, 移除不需要的item
* [oldItem] 如果有值, 则希望更新[oldItem]
* @return 返回null, 则会删除对应的[oldItem]返回与[oldItem]不一样的item, 则会替换原来的[oldItem]
* */
var updateOrCreateItem: (oldItem: DslAdapterItem?, data: Any?, index: Int) -> DslAdapterItem? =
{ oldItem, data, index ->
oldItem
}
核心处理方法, 这个方法用来处理, 更新当前界面相同的item, 还是删除当前界面的item, 还是在当前界面添加item. 注释写的很清楚了.
var adapterUpdateResult: (dslAdapter: DslAdapter) -> Unit = { dslAdapter ->
with(dslAdapter) {
if (dataItems.isEmpty() && headerItems.isEmpty() && footerItems.isEmpty()) {
//空数据
setAdapterStatus(DslAdapterStatusItem.ADAPTER_STATUS_EMPTY)
} else {
setAdapterStatus(DslAdapterStatusItem.ADAPTER_STATUS_NONE)
}
}
adapterCheckLoadMore(dslAdapter)
}
用于自动控制切换情感图
, 如果不需要, 可以置空.
var adapterCheckLoadMore: (dslAdapter: DslAdapter) -> Unit = { dslAdapter
dslAdapter.updateLoadMore(
updatePage,
if (updateDataList.isListEmpty()) 0 else (updateDataList?.size ?:
pageSize,
alwaysEnableLoadMore
)
}
用于自动控制加载更多
无更多
, 如果不需要, 可以置空.
可以通过以下方法直接使用UpdateDataConfig
:
dslAdapter.updateData{
//此时的上下文对象就是`UpdateDataConfig`
//此dsl区域内, 可以直接操作`UpdateDataConfig`对象的成员变量
//操作完之后, 界面会触发刷新
}
为了更简洁的使用方法, 我还提供了以下更贴心的API:
API声明:
/**
* 单一数据类型加载完成后, 调用此方法.
* 自动处理, 情感图切换, 加载更多切换.
*
* [itemClass] 渲染界面的`DslAdapterItem`
* [dataList] 数据列表, 数据bean, 会被自动赋值给`dslAdapter.itemData`成员
* [error] 是否有错误, 如果有错误, 将会根据已有数据量智能切换到错误情感图, 或者加载更多失败的情况
* [page] 当前Page参数 包含请求页码, 每页请求数据量
* [initItem] 根据`Item`的类型, 为自定义的数据结构赋值
* */
fun <Item : DslAdapterItem, Bean> DslAdapter.loadDataEnd(
itemClass: Class<Item>,
dataList: List<Bean>?,
error: Throwable?,
page: Page,
initItem: Item.(data: Bean) -> Unit = {}
) {
loadDataEndIndex(itemClass, dataList, error, page) { data, _ ->
initItem(data)
}
}
注释写的很清楚了, 下面看一下实战代码:
dslAdapter.loadDataEnd(DslSelectPoiItem::class.java, list, null, singlePage()) { bean ->
itemMapLocation = MapLocation.from(bean)
itemBottomInsert = 1 * dpi
itemDecorationColor = _color(R.color.lib_line_dark)
}
//因为demo没有加载更多, 所以使用`singlePage()`不触发`加载更多`
如此, 就结束了. 关注您该关注的, 多余的都是在浪费生命.
注意:
loadDataEnd
适合单类型Item
的界面, 多类型可以考虑使用updateData
更多使用代码, 请查看Demo
源码.
如有疑问, 请直接联系本人.