孟老板 BaseAdapter封装(五) ListAdapter
- BaseAdapter封装(一) 简单封装
- BaseAdapter封装(二) Header,footer
- BaseAdapter封装(三) 空数据占位图
- BaseAdapter封装(四) PageHelper
- BaseAdapter封装(五) ListAdapter
- BaseAdapter封装(六) Healer,footer for List
- BaseAdapter封装(七) ConcatAdapter 改建头尾
- BaseAdapter封装(八) Paging 分页
目录:
前言:
listAdapter?? 是的你没有听错... 算了不解释了,都1202年了; 它是 androidx.recyclerview.widget 包下为RecycleView服务的类;
ListAdapter 的优势:
1. 刷新列表只需要 submitList 这一个方法; 而避免原Adapter 增删改 的各种 notifyItem.. 操作;
2. AsyncListDiffer 异步计算新旧数据差异, 并通知 Adapter 刷新数据
3. 真正的数据驱动, 无论增删改查 我们只需要关心并操作数据集.
使用:
1.新建Adapter 继承 ListAdapter; 泛型提供 数据集实体类 和 自定义的 ViewHolder; 构造函数提供了 自定义的 DiffCallback()
DiffCallback 继承自 DiffUtil.ItemCallback 稍安勿躁后面会讲到; 跳转


class TestAdapter : ListAdapter<DynamicTwo, NewViewHolder>(DiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
TODO("Not yet implemented")
} override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
TODO("Not yet implemented")
}
}
2. onCreateViewHolder 的代码比较简单, MVVM模式, 直接返回 用 ViewDataBinding 构造的 ViewHolder; 只有一个可变参数, 即布局资源文件ID;
onBindViewHolder 的代码更简单, 拿到数据实体 并交给 ViewHolder 处理即可;
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): NewViewHolder {
return NewViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
R.layout.item_dynamic_img, parent, false
)
)
} override fun onBindViewHolder(holder: NewViewHolder, position: Int) {
holder.bind(getItem(position))
}
3. ViewHolder 的代码:
open class NewViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(item: BaseItem?) {
binding.setVariable(BR.item, item)
binding.executePendingBindings()
}
}
4. DiffCallback 代码:
class DiffCallback : DiffUtil.ItemCallback<DynamicTwo>() {
override fun areItemsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean {
return oldItem === newItem
}
override fun areContentsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean {
return !oldItem.hasChanged
}
}
5. 操作数据: Adapter 新建跟以往没有区别.
mAdapter = TestAdapter()
mDataBind.rvRecycle.let {
it.layoutManager = LinearLayoutManager(mActivity)
it.adapter = mAdapter
}
5.1 增删操作比较简单, 只需要更改数据集, 并 submitList 即可
注意: 这里需要用新数据集对象操作!!! 切记
fun addData(){
val data = mutableListOf<DynamicTwo>() //currentList 不需要判空, 它有默认的空集合
data.addAll(mAdapter.currentList)
repeat(10){
data.add(DynamicTwo())
}
mAdapter.submitList(data)
} fun deleteItem(position: Int){
val data = mutableListOf<DynamicTwo>()
data.addAll(mAdapter.currentList)
data.removeAt(position)
mAdapter.submitList(data)
} fun updateItem(position: Int){
//TODO 暂放
}
为什么这里必须要用新集合对象操作? 我们来看一下 submitList 的源码
public void submitList(@Nullable List<T> list) {
mDiffer.submitList(list);
} public void submitList(@Nullable final List<T> newList,
@Nullable final Runnable commitCallback) {
// incrementing generation means any currently-running diffs are discarded when they finish
final int runGeneration = ++mMaxScheduledGeneration; if (newList == mList) {
// nothing to do (Note - still had to inc generation, since may have ongoing work)
if (commitCallback != null) {
commitCallback.run();
}
return;
} final List<T> previousList = mReadOnlyList; // fast simple remove all
if (newList == null) {
//noinspection ConstantConditions
int countRemoved = mList.size();
mList = null;
mReadOnlyList = Collections.emptyList();
// notify last, after list is updated
mUpdateCallback.onRemoved(0, countRemoved);
onCurrentListChanged(previousList, commitCallback);
return;
} // fast simple first insert
if (mList == null) {
mList = newList;
mReadOnlyList = Collections.unmodifiableList(newList);
// notify last, after list is updated
mUpdateCallback.onInserted(0, newList.size());
onCurrentListChanged(previousList, commitCallback);
return;
}
.....
}
mList 为旧集合对象; 红字部分(JAVA 代码) 可以看出, 当新旧数据为同一对象时return 就不再往下执行了.
ListAdapter 认为新旧数组为同一对象时, nothing to do.
我们可以认为这是 ListAdapter 的一个特性. 也许它只是提醒我们 不要做无效刷新操作;
当然我们也可以重写 submitList 方法, 然后自动新建数据集.
5.2 DiffUtil
讲更新操作前, 需要先讲 DiffUtil
引用官方话术:
DiffUtil 是 ListAdapter 能够高效改变元素的奥秘所在。DiffUtil 会比较新旧列表中增加、移动、删除了哪些元素,然后输出更新操作的列表将原列表中的元素高效地转换为新的元素。
简单理解:
ListAdpater 就是通过 DiffUtil 计算前后集合的差异, 得出增删改的结果. 通知Adapter做出对应操作;
5.2.1 areItemsTheSame(): 比较两个对象是否是同一个 Item;
常见的比较方式: 可自行根据情况或个人习惯选用
1.比较内存地址: java(==) kotlin(===)
2.比较两个对象的 Id; 一般对象在库表中都有主键 ID 参数; 相同的情况下,必定为同一条记录;
3.equals: java(obj.equals(other)) kotlin(==)
5.2.2 areContentsTheSame(): 在已经确定同一 Item 的情况下, 再确定是否有内容更新;
网上给出的比较方式几乎全是 equals; 但 equals 运用不当根本刷新不了 Item;
1.当 areItemsTheSame() 选用 比较内存地址 的方式时, areContentsTheSame() 不能用equals方式;
2.当某个具体的 Item 更新时, 必定会替换为一个新实体对象时. 可以用 equals 方式;
也就是说,当我给某个动态条目点赞时, 必须要 copy 一个新的动态对象, 给新对象设置点赞状态为 true; 然后再用新对象替换掉数据集中的旧对象. equals 刷新才能奏效;
3.当更新某个Item, 不确定是否为新Item对象实体时, 不能用 equals 方式;
总结: 同一个内存地址的对象 equals 有个鸡儿用? 有个鸡儿用??
状态标记方式:
实体对象中增加: hasChanged: Boolean 字段; 当对象内容变化时,设置 hasChanged 为true; ViewHolder.bind()时,置为false;
给最终的 ViewHolder 和 DiffCallback
class DiffCallback : DiffUtil.ItemCallback<DynamicTwo>() {
override fun areItemsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean {
return oldItem === newItem
} override fun areContentsTheSame(oldItem: DynamicTwo, newItem: DynamicTwo): Boolean {
return !oldItem.hasChanged
}
} open class NewViewHolder(val binding: ViewDataBinding) : RecyclerView.ViewHolder(binding.root){
fun bind(item: BaseItem?, index: Int) {
item?.hasChanged = false
binding.setVariable(BR.item, item)
binding.executePendingBindings()
}
}
5.3 更新操作:
fun updateItem(position: Int){
val data = mutableListOf<DynamicTwo>()
data.addAll(mAdapter.currentList)
data[position].let {
it.title = "变变变 我是百变小魔女"
it.hasChanged = true
}
mAdapter.submitList(data)
}
最后:
ListAdapter 可完美的 由数据驱动 UI, 增删改可以放到 ViewModel中, 请求成功后直接操作数据集合 更新列表即可.
回到顶部
孟老板 BaseAdapter封装(五) ListAdapter的更多相关文章
- 孟老板 BaseAdapter封装 (一) 简单封装
BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...
- 孟老板 BaseAdapter封装 (二) Healer,footer
BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...
- 孟老板 BaseAdapter封装 (三) 空数据占位图
BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...
- 孟老板 BaseAdapter封装(四) PageHelper
BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...
- 孟老板 ListAdapter封装, 告别Adapter代码 (上)
BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...
- 孟老板 ListAdapter封装, 告别Adapter代码 (三)
BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...
- 孟老板 ListAdapter封装, 告别Adapter代码 (四)
BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...
- 孟老板 Paging3 (二) 结合Room
BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (二) 结合Room Paging 数据源不开放, 无法 ...
- 孟老板 Paging3 (一) 入门
BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (一) 入门 前言: 官方分页工具, 确实香. 但 ...
随机推荐
- POJ1456贪心(set或者并查集区间合并)
题意: 给你n商品,每个商品有自己的价值还有保质期,一天最多只能卖出去一个商品,问最大收益是多少? 思路: 比较好想的贪心,思路是这样,每一次我们肯定拿价值最大的,至于在那天拿 ...
- 在局域网内知道计算机的名字查找计算机的IP
第一步 nbtstat -a 计算机名字 第二步 nbtstat -c 可以看到计算机地址
- JVM虚拟机 类加载过程与类加载器
目录 前言 类的生命周期 类加载过程 加载 连接 验证 准备 解析 初始化 类加载器 三大类加载器 双亲委派模型 概念 为什么要使用双亲委派模型 源码分析 反双亲委派模型 参考 前言 类装载器子系统是 ...
- 关于有符号数和无符号数的转换 - C/C++
转载自:http://www.94cto.com/index/Article/content/id/59973.html 1.引例: 今天在做了一道关于有符号数和无符号数相互转换及其左移/右移的问题, ...
- PSP初体验:求交点
项目 内容 课程:北航2020春软件工程 博客园班级博客 作业:完成一个平面图形求交点的程序,体验PSP的过程 个人项目作业 我在这个课程的目标是 体验软件开发的全流程 这个作业在哪个具体方面帮助我实 ...
- VSCode配置MSVC+VSCode使用easyx库,2021.5.13日配置
VSCode配置MSVC+VSCode使用easyx库,2021.5.13日配置~~ 想必很多人和我一样,想用vscode编程c++,easyx库不支持MinGW,一般人都是直接使用vs2019安装e ...
- 原生JS和jQuery创建元素的方法
jQ创建元素的方法 1.原生代码 .creatElement('tr')` .innerHTML = '<h1>加油</h1>' document.write('<h1& ...
- 设置vue全局配置
全局配置 创建全局配置文件 vue.config.js 添加配置项
- python上下文管理协议,即with的详细使用
一.with obj as f: #代码块... 二.执行流程: 1.with obj --->触发obj.__enter__(),需要在obj里写__enter__(self),在它里边写返回 ...
- 寻找CPU使用率高的进程方法
寻找CPU使用率高的进程方法 发布时间: 2017-07-13 浏览次数: 1362 下载次数: 0 问题描述 节点报CPU使用率高,甚至出现"ALM-12016 CPU使用率超过阈值 ...