在android中listView/RecyclerView是最常用的ui控件,最常见的交互多是list的刷新,刷新和加载的交互涉及到adapter的刷新—-即notifyDataSetChanged()全局刷新
但是notifychaged有一些不可避免的弊端:
1.不会触发 RecyclerView 的局部更新的动画。
2.性能低,会刷新整个 RecyclerView 可视区域。
之前google有发布一个list中item局部刷新的Api:DiffUtil
DiffUtil完美解决了全局刷新的弊端;提高了UI 和data交互的效率和性能.下面 简单介绍下DiffUtil:
主要涉及两个类:
DiffUtil.Callback:具体用于限定数据集比对规则。
DiffUtil.DiffResult:比对数据集之后,返回的差异结果。
1.DiffUtil.Callback(对比新旧数据)
其中:
在 Callback 中,其实只需要实现 4 个方法:
getOldListSize():旧数据集的长度。
getNewListSize():新数据集的长度
areItemsTheSame():判断是否是同一个Item。
areContentsTheSame():如果是通一个Item,此方法用于判断是否同一个 Item 的内容也相同。
前两个是获取数据集长度的方法,这没什么好说的。但是后两个方法,主要是为了对应多布局的情况产生的,也就是存在多个 viewType 和多个 ViewHodler 的情况。首先需要使用 areItemsTheSame() 方法比对是否来自同一个 viewType(也就是同一个 ViewHolder ) ,然后再通过 areContentsTheSame() 方法比对其内容是否也相等。
其实 Callback 还有一个 getChangePayload() 的方法,它可以在 ViewType 相同,但是内容不相同的时候,用 payLoad 记录需要在这个 ViewHolder 中,具体需要更新的View。
areItemsTheSame()、areContentsTheSame()、getChangePayload() 分别代表了不同量级的刷新。
首先会通过 areItemsTheSame() 判断当前 position 下,ViewType 是否一致,如果不一致就表明当前 position 下,从数据到 UI 结构上全部变化了,那么就不关心内容,直接更新就好了。如果一致的话,那么其实 View 是可以复用的,就还需要再通过 areContentsTheSame() 方法判断其内容是否一致,如果一致,则表示是同一条数据,不需要做额外的操作。但是一旦不一致,则还会调用 getChangePayload() 来标记到底是哪个地方的不一样,最终标记需要更新的地方,最终返回给 DiffResult 。
当然,对性能要是要求没那么高的情况下,是可以不使用 getChangedPayload() 方法的。
2.DiffUtil.DiffResult(属性adapter 配合RecyclerView使用)
通过DiffUtil.Callback计算出差异然后直接作用域RecyclerView的adapter进行刷新
3代码实现
首先来看自己封装的一个RecyDiffCallback(继承自DiffUtil.Callback)
package com.example.diffutil.widget;
import android.support.v7.util.DiffUtil;
import java.util.List;
/**
* Created by houruixiang on 2017/8/21.
*/
public class RecyDiffCallback extends DiffUtil.Callback {
private List<String> newList;
private List<String> oldList;
public RecyDiffCallback(List<String> oldList, List<String> newList) {
this.oldList = oldList;
this.newList = newList;
}
@Override
public int getOldListSize() {
return oldList.size();
}
@Override
public int getNewListSize() {
return newList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldList.get(oldItemPosition).getClass().equals(newList.get(newItemPosition).getClass());
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
String oldStr = oldList.get(oldItemPosition);
String newStr = newList.get(newItemPosition);
return oldStr.equals(newStr);
}
}
以上代码是自己封装的,在构造中传入新旧数据,然后对比data的长度,接着对比item的type是否相同;最后对比内容;
下面看MainActivity中的核心代码
@Override
public void onClick(View view) {
mOld = mData.get(index);
index++;
index = index % mData.size();
mNew = mData.get(index);
Log.i("==onClick",String.valueOf(index));
recyAdapter.setData(mData.get(index));
DiffUtil.DiffResult diffResult = DiffUtil
.calculateDiff(new RecyDiffCallback(mOld, mNew),true);
diffResult.dispatchUpdatesTo(recyAdapter);
//mList.setAdapter(recyAdapter);
}
在代码中setData到adapter中
recyAdapter.setData(mData.get(index));
然后进行新旧data的对比
DiffUtil.DiffResult diffResult = DiffUtil
.calculateDiff(new RecyDiffCallback(mOld, mNew),true);
进行新旧数据集的对比 ,若三重对比(上面有介绍),然后进行diff的局部刷新:
diffResult.dispatchUpdatesTo(recyAdapter);
到这里RecyClerView+DiffUtil的局部刷新就介绍完了 是不是好用到飞起 ;
希望共同进步,欢迎大家宝贵的意见;