一个问题:

每次从服务器取到数据后,都是调用adapter.notifyDataSetChanged();进行刷新。那局部刷新(adapter.notifyItemChanged();)的这些东西不是白瞎了吗?对性能也不好,还没有动画。

怎么办:

用DiffUtil吧!号称可以进行局部刷新神器,让你的item 该刷新的地方就刷新,数据没有改变的地方不刷新(DiffUtil 内部调用了的局部刷新,还支持item动画哟!)。

怎么用:

  • 重写一个类:DiffUtil.Callback ,自己写,(注意打Log, 如果觉得自己菜的话)
    public class MyDiffCallback extends DiffUtil.Callback {//Thing 是adapter 的数据类,要换成自己的adapter 数据类private List<Thing> current;private List<Thing> next;public MyDiffCallback(List<Thing> current, List<Thing> next) {this.current = current;this.next = next;Log.d("数据c", current.toString());Log.d("数据n", next.toString());}/*** 旧数据的size*/@Overridepublic int getOldListSize() {return current.size();}/*** 新数据的size*/@Overridepublic int getNewListSize() {return next.size();}/*** 这个方法自由定制 ,* 在对比数据的时候会被调用* 返回 true 被判断为同一个item*/@Overridepublic boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {Thing currentItem = current.get(oldItemPosition);Thing nextItem = next.get(newItemPosition);return currentItem.getId() == nextItem.getId();}/***在上面的方法返回true 时,* 这个方法才会被diff 调用* 返回true 就证明内容相同*/@Overridepublic boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {Thing currentItem = current.get(oldItemPosition);Thing nextItem = next.get(newItemPosition);return currentItem.equals(nextItem);}}
  • 并创建它,
    MyDiffCallback callback = new MyDiffCallback(adapter.things, things);
    对比数据
    DiffUtil.DiffResult result = DiffUtil.calculateDiff(callback);
  • 然后刷新,完事儿
    result.dispatchUpdatesTo(adapter);

源码分析,使用请回:

使用起来,尤其面临大量数据刷新时,你会感到从所未有的高效和简洁但是呢?
why are you so niu bi ?
read the fucking source code
想了解原理只有阅读源码。阅读源码时,有一点讲究,不是拿着一个类直接阅读,而是揪其一点不断深入。

比如他是怎么对比的:

一般我看不懂英文,我会先点开这个方法,看见这个方法内部调用了一个方法:

image.png

点入new AdapterListUpdateCallback(adapter)

意外收获

发现有一个adapter 传入这个对象,看源码它把adapter 包了一层,实现了接口,调用了adapter局部刷新的方法。
难道dispatchUpdatesTo(new AdapterListUpdateCallback(adapter));里面通过这个包裹实现了局部刷新的?

 public final class AdapterListUpdateCallback implements ListUpdateCallback {@NonNullprivate final RecyclerView.Adapter mAdapter;/*** Creates an AdapterListUpdateCallback that will dispatch update events to the given adapter.** @param adapter The Adapter to send updates to.*/
public AdapterListUpdateCallback(@NonNull RecyclerView.Adapter adapter) {mAdapter = adapter;
}/** {@inheritDoc} */
@Override
public void onInserted(int position, int count) {mAdapter.notifyItemRangeInserted(position, count);
}/** {@inheritDoc} */
@Override
public void onRemoved(int position, int count) {mAdapter.notifyItemRangeRemoved(position, count);
}/** {@inheritDoc} */
@Override
public void onMoved(int fromPosition, int toPosition) {mAdapter.notifyItemMoved(fromPosition, toPosition);
}/** {@inheritDoc} */
@Override
public void onChanged(int position, int count, Object payload) {mAdapter.notifyItemRangeChanged(position, count, payload);
}

}

进入方法 dispatchUpdatesTo(new AdapterListUpdateCallback(adapter));

public void dispatchUpdatesTo(ListUpdateCallback updateCallback) {final BatchingListUpdateCallback batchingCallback;if (updateCallback instanceof BatchingListUpdateCallback) {//赋值batchingCallback = (BatchingListUpdateCallback) updateCallback;} else {// 转换赋值batchingCallback = new BatchingListUpdateCallback(updateCallback);//noinspection UnusedAssignmentupdateCallback = batchingCallback;}
...if (endX < posOld) {//传入这个方法,发现其实在里面有调用局部刷新等方法dispatchRemovals(postponedUpdates, batchingCallback, endX, posOld - endX, endX);}
...if (endY < posNew) {//传入这个方法,发现其实在里面有调用局部刷新等方法dispatchAdditions(postponedUpdates, batchingCallback, endX, posNew - endY,endY);}
...for (int i = snakeSize - 1; i >= 0; i--) {if ((mOldItemStatuses[snake.x + i] & FLAG_MASK) == FLAG_CHANGED) {//这里也是batchingCallback.onChanged(snake.x + i, 1,mCallback.getChangePayload(snake.x + i, snake.y + i));}}后面省略.....batchingCallback.dispatchLastEvent();}

我们发现BatchingListUpdateCallback类中这个dispatchLastEvent()方法中调用局部刷新,然后在 上面这个方法dispatchUpdatesTo(ListUpdateCallback updateCallback)最后一行调用了dispatchLastEvent()方法,代码中dispatchAdditions()dispatchRemovals()方法中均有调用batchingCallback 的刷新方法,意外收获局部刷新的秘密!

回到正题,他到底是怎样高效对比数据的呢?

查看完整代码

    public void dispatchUpdatesTo(ListUpdateCallback updateCallback) {//跳过final BatchingListUpdateCallback batchingCallback;if (updateCallback instanceof BatchingListUpdateCallback) {batchingCallback = (BatchingListUpdateCallback) updateCallback;} else {batchingCallback = new BatchingListUpdateCallback(updateCallback);// replace updateCallback with a batching callback and override references to// updateCallback so that we don't call it directly by mistake//noinspection UnusedAssignmentupdateCallback = batchingCallback;}//跳过final List<PostponedUpdate> postponedUpdates = new ArrayList<>();int posOld = mOldListSize;int posNew = mNewListSize;for (int snakeIndex = mSnakes.size() - 1; snakeIndex >= 0; snakeIndex--) {//这是啥?好像是通过他的 x y 进行刷新的,具体不晓得了final Snake snake = mSnakes.get(snakeIndex);final int snakeSize = snake.size;final int endX = snake.x + snakeSize;final int endY = snake.y + snakeSize;if (endX < posOld) {//进入方法,一通操作,除了刷新,没有数据对比dispatchRemovals(postponedUpdates, batchingCallback, endX, posOld - endX, endX);}if (endY < posNew) {//进入方法,一通操作,除了刷新,没有数据对比dispatchAdditions(postponedUpdates, batchingCallback, endX, posNew - endY,endY);}for (int i = snakeSize - 1; i >= 0; i--) {if ((mOldItemStatuses[snake.x + i] & FLAG_MASK) == FLAG_CHANGED) {// 没有数据对比batchingCallback.onChanged(snake.x + i, 1,//进入方法,一通操作,除了刷新,没有数据对比mCallback.getChangePayload(snake.x + i, snake.y + i));}}posOld = snake.x;posNew = snake.y;}batchingCallback.dispatchLastEvent();}

看一遍你会得到上面一样结果,这时候会注意到自己找的地方错了,好像是数据对比在这之前,而且会注意到final Snake snake = mSnakes.get(snakeIndex);这个对象,后面的代码是依照Snake 的x.y 等属性进行刷新,可以推断出mSnackes集合必然保存着对比数据后产生的结果,但是看它的注释一脸懵逼。

// The Myers' snakes. At this point, we only care about their diagonal sections.
// 迈尔斯的蛇。在这一点上,我们只关心它们的对角线部分。(对于这个集合的注释一脸懵逼)private final List<Snake> mSnakes;

通过这个集合,发现构造函数中对它进行的赋值:

    DiffResult(Callback callback, List<Snake> snakes, int[] oldItemStatuses,int[] newItemStatuses, boolean detectMoves) {//集合被赋值mSnakes = snakes;···// 里面有add(snacke) 操作,但是看方法注释是针对于0 的情况,直接忽略。addRootSnake();// 这方法里面 调用了重写的areContentsTheSame(oldItemPos, newItemPos)(对比item内容是否有改变)//,并把状态存入数组,用的时候直接取出。findMatchingItems();}

追踪构造函数(DiffResult())被调用之处是calculateDiff(Callback cb, boolean detectMoves)
发现calculateDiff(Callback cb, boolean detectMoves)方法
DiffUtil.DiffResult result = DiffUtil.calculateDiff(callback); 调用(我们创建的时候,手动调的,请看前面使用部分文章)

数据如何对比依然是谜

推测知道snakes 集合是数据对比后的结果,它用来判断item是否直接刷新
追踪snakes集合产生过程必然就知道了对比过程
接着看caulateDiff(callback cb ,boolean deteMoves)方法

public static DiffResult calculateDiff(Callback cb, boolean detectMoves) {// 这里调用了我们使用时重写的方法,现在知道,重写的方法的用处了final int oldSize = cb.getOldListSize();final int newSize = cb.getNewListSize();//结果集final List<Snake> snakes = new ArrayList<>();···// 一通操作···  // 得到snake   ,重要final Snake snake = diffPartial(cb, range.oldListStart, range.oldListEnd,range.newListStart, range.newListEnd, forward, backward, max);if (snake != null) {if (snake.size > 0) {//添加到集合snakes.add(snake);}//转成 x ,y 信息得到 snake.x += range.oldListStart;snake.y += range.newListStart;// 一通转化 和保存,返回数据结果集····return new DiffResult(cb, snakes, forward, backward, detectMoves);}

发现diffPartial(cb, range.oldListStart, range.oldListEnd,range.newListStart, range.newListEnd, forward, backward, max);方法,计算得snake 对象然后添加到集合中, 但是打开这个方法里面你会发现里面全是计算:

private static Snake diffPartial(Callback cb, int startOld, int endOld,int startNew, int endNew, int[] forward, int[] backward, int kOffset) {···for (int d = 0; d <= dLimit; d++) {for (int k = -d; k <= d; k += 2) {···while (x < oldSize && y < newSize//这里调用了重写的方法耶,判断是否是同一个item&& cb.areItemsTheSame(startOld + x, startNew + y)) {x++;y++;}}}for (int k = -d; k <= d; k += 2) {for (int k = -d; k <= d; k += 2) {···while (x > 0 && y > 0//这里调用了重写的方法耶,判断是否是同一个item&& cb.areItemsTheSame(startOld + x - 1, startNew + y - 1)) {x--;y--;}}}···
}

到这里diffUtil 的源码才算大概了解了。

RecyclerView 配合 DiffUtil,RecyclerView局部刷新相关推荐

  1. php后台登录页修改成ajax,使用php后台给自己做一个页面路由,配合ajax实现局部刷新。(示例代码)...

    今天就要放假了,把近来囤积的小玩意儿总结整理一下. 在请求一个html页面来嵌入到当前页会有一个问题,就是跟随请求过来的html他的样式表和脚本会失效.是因为文档加载的先后顺序等问题造成的.因此,加载 ...

  2. RecyclerView局部刷新机制

    之前在使用RecyclerView的遇到过一个问题,使用notifyItemChanged刷新数据的时候会出现重影或者闪烁的现象. 这个问题很容易出现,当我们的列表中有进度显示(比如下载),这时候需要 ...

  3. android局部布局刷新,Android之RecyclerView的局部刷新

    局部更新方法 1.使用Diffutil进行数据的比较 [Android]详解7.0带来的新工具类:DiffUtil 1.1一个相对完整的例子 Android高性能列表:RecyclerView + D ...

  4. android局部布局刷新,Android RecyclerView 局部刷新分析

    前情回顾 之前写的 PowerAdapter 和 SelectPowerAdapter 从创建到现在,已经两年多,期间发生了翻天覆地的变化.一开始,我把 SwipeRefreshLayout 和 Re ...

  5. RecyclerView局部刷新和原理介绍

    RecyclerView局部刷新和原理介绍 一.引言 二.局部刷新的正确使用姿势 三.局部刷新的原理 3.0 前提 3.1 RecyclerView与Adapter建立观察者模式 3.2 onItem ...

  6. RecyclerView局部刷新

    在RecyclerView中,我们时常会用到局部刷新,我们大多数是使用:notifyItemChanged. 我在使用这个局部刷新过程中突然发现我有几个notifyItemChanged没有效果,我就 ...

  7. RecyclerView局部刷新的坑

    话说有图有真相,首先来对比一下局部刷新前后的效果: 优化之前的效果: 优化之后的效果: 可以看到,优化之后,列表中的这张大图不在有一闪一闪亮晶晶的效果了! 那么,这是如何做到的呢?这是本文的重点,本文 ...

  8. android局部动态刷新,RecyclerView的局部刷新爬坑之路简述

    RecyclerView的局部刷新爬坑之路简述,实际上RecyclerView做局部刷新是非常容易的,其实就是使用好带payload参数的这个notifyItemRangeChanged方法,以及ov ...

  9. 安卓 RecyclerView局部刷新

    在我们的开发中,RecyclerView是使用非常频繁的,除了常用的列表展示之外,很多时候我们要根据item的操作进行列表的刷新,比如勾选列表的勾选.删除或者根据操作动态设置某个item的布局.这时候 ...

最新文章

  1. 快学Scala-第八章 继承
  2. chapter 2 自定义数据类型
  3. php 可以动态的new一个变量类名
  4. +++程序员高手修炼之路
  5. 软件数控编程_这么多CNC数控编程软件, 你觉得哪个好?
  6. java代码创建jar_Java 创建ZIP和JAR文件
  7. Kali linux 2016.2(Rolling)之 Nessus安装及Plugins Download Fail 解决方法
  8. CSS3动画的常见属性(CSS3)
  9. 好好编程-物流项目01【搭建maven工程】
  10. 【ajax】6.IE缓存问题解决
  11. summernote 富文本编辑器上传七牛云服务器
  12. XSS Filter Evasion Cheat Sheet 中文版
  13. 如何用电脑破解WiFi
  14. DAO年终盘点:光环加身,道阻且长 |链捕手
  15. 【云原生】docker+k8微服务容器化实战
  16. Mega软件操作教程
  17. 网络安全学习笔记——红队实战攻防(上)
  18. oracle试题和答案,Oracle面试题及答案
  19. python学习总结7 - 输入与输出【格式化字符串及读写文件】
  20. MCS:离散随机变量——Bernoulli分布

热门文章

  1. python列表比较方法_python列表常用方法
  2. C++ 请以pass-by-reference-to-const替换pass-by-value
  3. Single-stage目标检测网络YOLO相关背景知识
  4. 视频教程的录制与制作的流程,方法,经验
  5. Suzy找到实习了吗 Day23 | 二叉树最后一节!669. 修剪二叉搜索树,108. 将有序数组转换为二叉搜索树,538. 把二叉搜索树转换为累加树
  6. 2022李宏毅机器学习hw1--COVID-19 Cases Prediction
  7. Windows:MULTIPROCESSOR CONFIGURATION NOT SUPPORTED蓝屏(32位(win10/LTSC 2019/LTSC 2021))
  8. 如何系统化学Python?
  9. 艺赛旗RPA离线识别普通验证码
  10. ListBox优化初步(二)