嗨,好久不见,突然发现我的上一篇博文是16年的3月份,至今已有半年多了。啊哈,其实心里是一直惦记着这里的。好久没写博文了,由于之前公司员工变动,然后导致Android开发这边我一个人弄了这么久,又加上大版本的巨大压力,不得不暂时放弃撰写博文。不过好消息是现在Android开发已经有4个人了,相对之前我要做的事情已经变得很少很少了。哈哈,所以继续吧,大家也都相比一直认可博文吧,因为自己就算有时候东西掌握的再好,随着时间的流逝,也会一点一点忘记的。养成好习惯,把工作中遇到的问题,记下来,或者说遇到好的点当做笔记记下来,这都是财富!
嗯,对对对,快点进入主题,今天我想带大家看看的是DiffUtil这个新的工具类。简单来说,DiffUtil是support-v7:24.2.0中的新工具类,它用来比较两个数据集,寻找出旧数据集到新数据集的最小变化量。官方的意思就是说DiffUtil,它可以计算两个列表的区别和输出一个更新操作列表,将第一个列表转换成第二个。当然官方也说了If the lists are large, this operation may take significant time so you are advised to run this on a background thread, get the DiffUtil.DiffResult then apply it on the RecyclerView on the main thread.当列表非常大的时候是建议放在子线程,然后再在主线程刷新RecyclerView。
以后也是这样,前面最好在第一个内容里面就让大家看一下效果图,对这东西有兴趣咱们再看代码是吧。东西的话我就直接拿我公司的展示啦(弱弱的问一下领导会打我吗O(∩_∩)O)
效果想必大家也应该能看到了,当RecyclerView的Item增加或者删除的时候不再是闪一下刷新,而是又一个插入或者删除的小动画效果,轻度的衔接,我觉得是比较好的。好了,接下来我们看看如何实现。
首先你应该了解到的是先去把你的support-v7:24.2.0包升级到,升级我就不多说了哈。之前用RecyclerView刷新的时候想必有很多人就是无脑的直接刷新吧,直接调用mAdapter.notifyDataSetChanged(),对于直接调用我个人觉得也没啥问题,有人可能会说效率啥的,其实RecyclerView本身对数据就是有一个比较的,无非就是有点尴尬,刷新列表不优雅。当然,官方肯定是不建议我们直接这么用的,因为你要知道RecyclerView的Adapter可是有4个刷新的方法。
现在我们使用DiffUtil也非常简单,只要在原来的mAdapter.notifyDataSetChanged() 修改如下:
DiffUtil.DiffResult diffResult =
DiffUtil.calculateDiff(new DiffCallBack(oldDatas, newDatas), true);
diffResult.dispatchUpdatesTo(mAdapter);
接下来的事情就交给DiffUtil去做就行了,其实稍微深入一下就能发现,其实DiffUtil就是帮我们去只能调用了Adapter的这4个方法,很显然就是这几个方法都是定向刷新,并且都是伴随着一点小小的动画的,刷新效率也得到了巨大的提升。
adapter.notifyItemRangeInserted(position, count);
adapter.notifyItemRangeRemoved(position, count);
adapter.notifyItemMoved(fromPosition, toPosition);
adapter.notifyItemRangeChanged(position, count, payload);
然后呢,我们唯一要做的一件事就是去继续一个DiffUtil.Callback的类,实现它的四个abstract方法。这也是本篇文章着重要讲的内容,我遇到的坑也就是在这里。
public abstract static class Callback {
public abstract int getOldListSize();
public abstract int getNewListSize();
public abstract boolean areItemsTheSame(int oldItemPosition, int newItemPosition);
public abstract boolean areContentsTheSame(int oldItemPosition, int newItemPosition);
}
以上4个方法前2个不多说,就是直接返回新老列表的长度。
现在主要来讲讲后面2个,第一个是areItemsTheSame()首先,这边大致意思就是item是否相同?这里不看他的字面意思哈。我对于这个方法的理解就是当这边返回true时,才会走下面的areContentsTheSame(),如果这边直接返回false,也将不再走areContentsTheSame(),也就是说会直接强制刷新这个item的内容,整个效果就是该item突然移除,然后又在该位置出现一个新的item。一般来说如果可以直接确定2个列表该item的数据完全不同时,就可以直接返回false。比如说你的RecyclerView用到了多布局,然后能非常明确的确定该item将会变成另外一个布局。如果2个列表的数据只有部分不相同,可以返回true,去走areContentsTheSame(),然后在里面判断数据是否相同。比如说我上面截图中一个商品的数量从1变成2,那就可以返回true,在areContentsTheSame()里面再去刷新。
areContentsTheSame()这个方法相对来说简单一点,就是比较两个item的内容是否相同,就像上面说的,我截图里面的一个商品数量从1变成2,那么不用刷新整个item布局,在areItemsTheSame()返回true好让程序继续走到areContentsTheSame()方法中,然后你再在这个方法中去比较item的数据,如果是完全一样,那就返回true,不刷新该item,如果有不同,则返回false,轻量级刷新该item。
最后简单的贴一下使用方法
public class DiffCallBack extends DiffUtil.Callback {
private List mOldDatas, mNewDatas;
public DiffCallBack(List mOldDatas, List mNewDatas) {
this.mOldDatas = mOldDatas;
this.mNewDatas = mNewDatas;
}
@Override
public int getOldListSize() {
return mOldDatas != null ? mOldDatas.size() : 0;
}
@Override
public int getNewListSize() {
return mNewDatas != null ? mNewDatas.size() : 0;
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
/**
*这边根据你的需求去判断到底是刷新整个item,还是该item只有一点点数据的变化,从而去下面的方法判断是否需要刷新
*我的理解就是该item从旧数据到新数据我们用到的layout都变了,就是多布局,那就这边直接返回false,刷新整个item。如果返回true,讲继续执行下面的方法去判断。
**/
return true;
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
//这边也是一样的,如果返回true,则不刷新该item,如果返回false,则只刷新该item,并且是一个动作非常小的刷新
return true;
}
}
源码我就不贴啦(毕竟没有写dome,直接拿公司项目开刀的),东西很简单的,主要是搞清楚3 4 方法的区别,以及何时正确的返回true或false,让你的刷新更加自然流畅,效率也更加高。要知道我第一次用这东西的时候可是一个一个返回值试出来的,最终才搞清楚到底这个小坑。