目录:

  ListAdapter 简介及优势

  开始使用

  DiffUtil 

  areContentsTheSame 不能更新条目的原因

前言:

  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的更多相关文章

  1. 孟老板 BaseAdapter封装 (一) 简单封装

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  2. 孟老板 BaseAdapter封装 (二) Healer,footer

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  3. 孟老板 BaseAdapter封装 (三) 空数据占位图

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  4. 孟老板 BaseAdapter封装(四) PageHelper

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  5. 孟老板 ListAdapter封装, 告别Adapter代码 (上)

    BaseAdapter封装(一) 简单封装 BaseAdapter封装(二) Header,footer BaseAdapter封装(三) 空数据占位图 BaseAdapter封装(四) PageHe ...

  6. 孟老板 ListAdapter封装, 告别Adapter代码 (三)

    BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...

  7. 孟老板 ListAdapter封装, 告别Adapter代码 (四)

    BaseAdapter系列 ListAdapter封装, 告别Adapter代码 (一) ListAdapter封装, 告别Adapter代码 (二) ListAdapter封装, 告别Adapter ...

  8. 孟老板 Paging3 (二) 结合Room

    BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (二)  结合Room Paging 数据源不开放, 无法 ...

  9. 孟老板 Paging3 (一) 入门

    BaseAdapter系列 ListAdapter系列 Paging3 (一) 入门 Paging3 (二) 结合 Room Paging3 (一)  入门 前言: 官方分页工具,  确实香.   但 ...

随机推荐

  1. POJ2553 强连通出度为0的应用

    题意:       给你一个有向图,然后问你有多少个满足要求的点,要求是: 这个点能走到的所有点都能走回这个点,找到所有的这样的点,然后排序输出. 思路:       可以直接一遍强连通缩点,所点之后 ...

  2. POJ 2396 构造矩阵(上下流)

    题意:       要求构造一个矩阵,给你行和,列和,还有一些点的上下范围,输出一个满足题意的矩阵. 思路:       这个题目很经典,这是自己看上下流后接触的第一道题,感觉很基础的一道题目,现在我 ...

  3. 使用BurpSuite抓取HTTPS网站的数据包

    昨天面试,技术官问到了我如何使用BurpSuite抓取https网站的数据包,一时间没能回答上来(尴尬!).因为以前https网站的数据包我都是用Fiddler抓取的,Fiddlert自动帮我们配置好 ...

  4. RDPInception攻击手法

    在讲RDPInception攻击手段之前,我们先了解一下RDP远程桌面(Remote Desktop Protocol)协议.RDP远程桌面协议(Remote Desktop Protocol)是一个 ...

  5. 8.PHP图像处理

    PHP图像处理 GD2 Jpgraph 创建一个画布: <?php    header('content-type:image/gif');    //echo "你好";  ...

  6. WindowsPE 第五章 导出表编程-1(枚举导出表)

    导出表编程-1-枚举导出表 开始前先回忆一下导出表: 1.枚举dll函数的导出函数名字: 思路: (1)加载dll到内存里. (2)获取PE头,逐步找到可选头部. (3)然后找到里面的第一个结构(导出 ...

  7. Windowsw核心编程 第13章 Windows内存结构

    第1 3章 Wi n d o w s的内存结构 13.1 进程的虚拟地址空间 每个进程都被赋予它自己的虚拟地址空间.对于 3 2位进程来说,这个地址空间是4 G B,因为3 2位指针可以拥有从0 x  ...

  8. Python爬虫之-动态网页数据抓取

    什么是AJAX: AJAX(Asynchronouse JavaScript And XML)异步JavaScript和XML.过在后台与服务器进行少量数据交换,Ajax 可以使网页实现异步更新.这意 ...

  9. <input type="file" id="fileID">文本框里的值清空方法

    一般情况下,不允许通过脚本来对文件上传框赋值. 下面是一个变通的方法.就是创建一个新的input type="file" 把原来的替换掉. <!DOCTYPE html PU ...

  10. SqlServer数据库主从同步

    分发/订阅模式实现SqlServer主从同步 在文章开始之前,我们先了解一下几个关键的概念: 分发服务器分发服务器是负责存储在同步过程中所用复制信息的服务器.可以比喻成报刊发行商. 分发数据库分发数据 ...