孟老板 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 (一) 入门 前言: 官方分页工具, 确实香. 但 ...
随机推荐
- CVE-2013-2551:Internet Explore VML COALineDashStyleArray 整数溢出漏洞简单调试分析
0x01 2013 Pwn2Own 黑客大赛 在 Pwn2Own 的黑客大赛上,来自法国的 VUPEN 安全团队再一次利用 0day 漏洞攻破 Windows8 环境下的 IE10 浏览器,这一次问题 ...
- 3.逆向分析Hello World!程序-下
5.继续补充,常用操作指令: Ctrl+G Go to 移动到指定地址,用来查看代码或内存,运行时不可用 F4 Execute till Cursor 执行到光标位置, ...
- TCP的握手和挥手
三次握手 三次握手具体过程是什么? 客户端发送一个数据包 将SYN置成1,表示希望建立连接 这个包中的序列号是X 服务器收到客户端发来的数据包 通过SYN得知这是一个建立连接的请求 于是发送一个响应包 ...
- NIOSII IDE在WIN7下 couldn't allocate heap
首先,所有的文件夹都不能有空格和中文 其次,出现这些SB错误 make -s all includes 3 [main] ? (3732) c:\altera\91\quartus\bin\cygwi ...
- 攻防世界(一)baby_web
攻防世界系列:baby_web 方法一: 按照提示,初始界面自然想到index.php,访问后界面(注意到URL)仍是1.php 打开hackbar查看响应,发现确实有index.php点开看到了Fl ...
- Docker存储(4)
一.docker存储资源类型 用户在使用 Docker 的过程中,势必需要查看容器内应用产生的数据,或者需要将容器内数据进行备份,甚至多个容器之间进行数据共享,这必然会涉及到容器的数据管理 (1)Da ...
- 云计算OpenStack---维护及错误排查(13)
错误一:删除僵尸卷 在openstack dashboard中正常删除实例,未删除卷,然后重启了服务器,出现BUG,卷被附加给了'NONE',并且无法删除,无法更新. 既然log中已经提示无法删除卷的 ...
- strcpy和memcpy的区别-(转自stone Jin)
strcpy和memcpy都是标准C库函数,它们有下面的特点.strcpy提供了字符串的复制.即strcpy只用于字符串复制,并且它不仅复制字符串内容之外,还会复制字符串的结束符. 已知strcpy函 ...
- 5.7 echo:显示一行文本
echo命令 能将指定的文本显示在Linux命令行上. -n 不要自动换行 -E 不解析转义字符(默认参数) -e 若字符串中出现以下字符,则需要进行特别处理,而不会将它 ...
- 【错误解决】The prefix "context" for element "context:component-scan" is not bound
在配置spring相关的applicationContext.xml文件时报以上错误 原因是缺失context的namespace. http://www.springframework.org/sc ...