本文作者
作者:狼窝山下的青年
链接:
https://juejin.cn/post/7206391499264426041
本文由作者授权发布。
本篇博客将从简单到高级,介绍使用 DiffUtil 的基本流程以及一些高级用法,帮助开发者更好地使用 DiffUtil。
DiffUtil 是一个用于计算两个列表之间差异的实用工具类。它通过比较两个列表的元素,找出它们之间的差异,并生成更新操作的列表,以便进行最小化的更新操作。
在使用 RecyclerView 时,如果数据集发生变化,我们通常需要调用 notifyDataSetChanged() 或者 notifyItemRangeChanged() 等方法进行刷新操作。但是这样做会导致整个列表都被刷新,即使只有一部分数据发生了变化,也会重新绘制整个列表,造成性能浪费。DiffUtil 可以帮助我们只更新发生变化的部分,从而减少不必要的刷新操作,提高性能。当我们面临这个问题时可以考虑DiffUtil。
• public boolean getMoveDetectionFlag():设置是否开启移动操作的检测。如果设置为 true,DiffUtil 会检测数据集中元素的移动操作,并生成移动操作的更新列表。但是,开启移动操作的检测会增加计算量,可能会影响性能。
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private List<String> mOldList;
private List<String> mNewList;
// 构造方法,初始化数据集
public MyAdapter(List<String> oldList, List<String> newList) {
mOldList = oldList;
mNewList = newList;
}
// ViewHolder,用于缓存列表项的视图
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView mTextView;
public ViewHolder(View itemView) {
super(itemView);
mTextView = itemView.findViewById(R.id.text_view);
}
}
// 创建 ViewHolder
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_layout, parent, false);
return new ViewHolder(view);
}
// 绑定 ViewHolder
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
String text = mNewList.get(position);
holder.mTextView.setText(text);
}
// 获取数据集大小
@Override
public int getItemCount() {
return mNewList.size();
}
// 更新数据集
public void updateList(List<String> newList) {
// 计算差异
DiffUtil.Callback callback = new MyCallback(mOldList, newList);
DiffUtil.DiffResult result = DiffUtil.calculateDiff(callback);
// 更新数据集
mOldList = mNewList;
mNewList = newList;
result.dispatchUpdatesTo(this);
}
// 自定义的 DiffUtil.Callback
private static class MyCallback extends DiffUtil.Callback {
private List<String> mOldList;
private List<String> mNewList;
public MyCallback(List<String> oldList, List<String> newList) {
mOldList = oldList;
mNewList = newList;
}
@Override
public int getOldListSize() {
return mOldList.size();
}
@Override
public int getNewListSize() {
return mNewList.size();
}
@Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
}
@Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return mOldList.get(oldItemPosition).equals(mNewList.get(newItemPosition));
}
}
}
• public static class AsyncDiffResult extends AsyncTask<Void, Void, DiffUtil.DiffResult>:异步计算差异的工具类,可以在子线程中进行差异计算,并在主线程中更新 UI。
class ChatListAdapter : RecyclerView.Adapter<ChatListAdapter.ViewHolder>() {
private var messageList: List<ChatMessage> = emptyList()
fun submitListAsync(newList: List<ChatMessage>) {
val callback = ChatMessageDiffCallback(messageList, newList)
val asyncTask = object : AsyncTask<Void, Void, DiffUtil.DiffResult>() {
override fun doInBackground(vararg voids: Void): DiffUtil.DiffResult {
return DiffUtil.calculateDiff(callback)
}
override fun onPostExecute(diffResult: DiffUtil.DiffResult) {
messageList = newList
diffResult.dispatchUpdatesTo(this@ChatListAdapter)
}
}
asyncTask.execute()
}
// ...
}
使用 AsyncTask 来异步计算差异,并在计算完成后更新数据集。如果我们的数据集比较大,可以使用这种方法来避免阻塞主线程。注意,在使用异步计算时,我们不能直接调用 notifyDataSetChanged() 方法来刷新列表,而是需要通过 DiffUtil.DiffResult 的 dispatchUpdatesTo() 方法来更新列表。
6. 避免在 UI 线程中进行差异计算。DiffUtil 的差异计算可能会比较耗时,如果在 UI 线程中进行计算,就会导致应用的卡顿,影响用户体验。所以,在使用 DiffUtil 时,最好将差异计算放在一个异步任务中进行,或者使用其他方式来避免阻塞 UI 线程。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
点击 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!