孟老板 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 (一) 入门 前言: 官方分页工具, 确实香. 但 ...
随机推荐
- APK程序Dex文件无源码调试方法讨论
那些不靠谱的工具 先来说说那些不靠谱的工具,就是今天吭了我小半天的各种工具,看官上坐,待我细细道来.IDA pro IDA pro6.6之后加入了dex动态调试功能,一时间普天同庆.喜大普奔.兴奋之后 ...
- UVA10780幂和阶乘
题意: 输入两个整数n,m(1<m<5000,0<n<10000)求最小的k使得m^k是n!的因子. 思路: 比较容易想,一开始手残wa了好几次,我们直接 ...
- windows-API劫持(API-HOOK)
API Hook ApiHook又叫做API劫持,也就是如果A程序调用了B.cll里面的C函数,我们可以做到当A调用C函数执行的时候,直接执行我们自己事先准备好的函数,之后我们在执行真正的C,当然我们 ...
- .NET之默认依赖注入
介绍 不要依赖于具体的实现,应该依赖于抽象,高层模块不应该依赖于底层模块,二者应该依赖于抽象.简单的说就是为了更好的解耦.而控制反转(Ioc)就是这样的原则的其中一个实现思路, 这个思路的其中一种实现 ...
- (邹博ML)数学分析与概率论
机器学习入门 深度学习和机器学习? 深度学习在某种意义上可以认为是机器学习的一个分支,只是这个分支非常全面且重要,以至于可以单独作为一门学科来进行研究. 回忆知识 求解S. 对数函数的上升速度 我们使 ...
- Day003 JavaDoc
JavaDoc javadoc命令是用来生成自己的Api文档的 参数信息 @author 作者名 @version 版本号 @since 指明需要最早使用的jdk版本 @param 参数名 @retu ...
- JVM什么叫安全检测点
[deerhang] 在JVM的垃圾回收阶段,GC线程首先要进行对象的可达性分析.为了避免多线程对可达性分析的影响引出了安全点检测的概念 当GC线程进行GC前,需要等待其他线程进入安全点.例如JVM调 ...
- STL实现的底层数据结构简介
STL实现的底层数据结构简介 C++ STL 的实现: 1.vector 底层数据结构为数组 ,支持快速随机访问 2.list 底层数据结构为双向链表,支持快速增删 3.deque 底层数 ...
- Msf--永恒之蓝 ms17_010
|>>>中华人民共和国网络安全法<<<|警告:请勿用于非法用途,后果自负! 简介 一.概述 永恒之蓝是指2017年4月14日晚,黑客团体Shadow Brokers ...
- Ansible_使用文件模块将修改文件复制到受管主机
一.描述常用文件模块 1.常用文件模块 模块名称 模块说明 blockinfile 插入.更新或删除由可自定义标记线包围的多行文本块 copy 将文件从本地或远程计算机复制到受管主机上的某个位置.类似 ...