RecyclerView:使用DiffUtil实现快速更新和差异化更新

DiffUtil的作用是比较两个数据列表并能计算出一系列将旧数据表转换成新数据表的操作。它不再是简单数据更新,而是根据数据的变化去调用RecyclerView不同的刷新方法,将RecyclerView的个性化体现的淋漓尽致

在24.2.0支持包更新中,Google在RecyclerView的支持包中新增了DiffUtil类用来进行数据刷新,因为以前在使用RecyclerView的时候遇到过坑,尤其是下拉刷新的时候有时候会报错,因此对这个类报很大的期待。但是查看源码后不禁再次对Google的程序员深深的佩服,原理大分部都看懂了,但是算法不是我强项,知道他怎么对比的,但是速度优化方面就没有具体看了

Google对这个类的优化还是很给力的,下面是谷歌官网给出的在Nexus 5X M系统上进行运算的时长:

100项数据,10处改动:平均值0.39ms,中位数:0.35ms。
100项数据,100处改动:
打开了移位识别时:平均值:3.82ms,中位数:3.75ms。
关闭了移位识别时:平均值:2.09ms,中位数:2.06ms。
1000项数据,50处改动:
打开了移位识别时:平均值:4.67ms,中位数:4.59ms。
关闭了移位识别时:平均值:3.59ms,中位数:3.50ms。
1000项数据,200处改动:
打开了移位识别时:平均值:27.07ms,中位数:26.92ms。
关闭了移位识别时:平均值:13.54ms,中位数:13.36ms。

当数据集较大时,你应该在后台线程计算数据集的更新。

1、数据对比

使用DiffUtil首先要继承一个抽象类DiffUtil.CallbackDiffUtil是通过这个类来识别判断新旧2个集合有何不同的

public class ItemDiffCallBack extends DiffUtil.Callback {
    private List<Item> mOldList;
    private List<Item> mNewList;
    private final String TAG = getClass().getSimpleName();
    public ItemDiffCallBack(List<Item> oldList, List<Item> newList) {
        this.mOldList = oldList;
        this.mNewList = newList;
    }
    @Override
    public int getOldListSize() {
        return mOldList == null ? 0 : mOldList.size();
    }
    @Override
    public int getNewListSize() {
        return mNewList == null ? 0 : mNewList.size();
    }
    //这个是用来判断是否是一个对象的
    @Override
    public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
        return mOldList.get(oldItemPosition).id == mNewList.get(newItemPosition).id;
    }
    //这个是用来判断相同对象的内容是否相同
    @Override
    public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
        String oldContent = mOldList.get(oldItemPosition).content();
        String newContent = mNewList.get(newItemPosition).content();
        Log.i(TAG, "oldContent:" + oldContent + " newContent:" + newContent);
        return TextUtils.equals(oldContent ,newContent );
    }
    //找出其中的不同
    @Nullable
    @Override
    public Object getChangePayload(int oldItemPosition, int newItemPosition) {
        Item oldItem = mOldList.get(oldItemPosition);
        Item newItem = mNewList.get(newItemPosition);
        Bundle diffBundle = new Bundle();
        if (!TextUtils.equals(oldItem.content(), newItem.content())) {
            diffBundle.putString("content", newItem.content());
        }
        return diffBundle;
    }
}

代码比较简单,主要的地方我也做了注释,简单的解释一下,这个东西并不一定要2个对象完全相同,只需要你界面上显示的数据相同就可以了,毕竟这个只是用来刷新界面的,你对比的数据越少,速度就会越快。

2、Adapter刷新以及差异化更新

通过上面的类,DiffUtil可以得到一个变更集合DiffResultDiffResult会根据数据的增删改去调用AdapternotifyItemRangeInsertednotifyItemRangeRemovednotifyItemMovednotifyItemRangeChanged,Adapter中要重写onBindViewHolder3个参数的方法

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position, List<Object> payloads) {
    //判断数据更改是否为空,说明是新增的,直接去绑定数据
    if (payloads == null || payloads.isEmpty()) {
        onBindViewHolder(holder,position);
        Log.i(TAG, "position:" + position + " payloads is empty");
        return;
    }
    if (!(holder instanceof ItemViewHolder)) {
        return;
    }
    //如果不为空,说明有部分数据发生了更改,那么只要根据数据去更新变更的UI即可
    ItemViewHolder viewHolder = (ItemViewHolder) holder;
    Bundle bundle = (Bundle) payloads.get(0);
    String content = bundle.getString("content");
    viewHolder.tvPosition.setText(content);
    Log.i(TAG, "position:" + position + " payloads is not empty");
}

我看了很多博客(虽然大部分都是转载的),这个方法中他们都首先去调用了绑定方法,然后才去判断payloads是否为空,这样的话就失去了DiffUtil一大特性,也牺牲了一部分性能

这个方法其实还有其他的用处,例如你这个viewHolder里面有3个控件有动画,但是只有一个数据发生了变化,如果整体notify,那么3个动画都会去播放,在这里就可以只播放一个动画,让用户更清楚的知道是哪个数据发生了改变,差异化在这个地方体现的淋漓尽致

3、使用

DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new ItemDiffCallBack(oldItemList, mItemList));
diffResult.dispatchUpdatesTo(mItemAdapter);

使用很简单就不用多说了

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,456评论 5 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,370评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,337评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,583评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,596评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,572评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,936评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,595评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,850评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,601评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,685评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,371评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,951评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,934评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,167评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,636评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,411评论 2 342

推荐阅读更多精彩内容