android-RecyclerView的DiffUtil差异化工具使用

2024-01-02 02:59

本文主要是介绍android-RecyclerView的DiffUtil差异化工具使用,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

没有效果图的示例简直就是扯淡

在这里插入图片描述

有点模糊,大家凑活看吧。。。

DiffUtil是什么?

DiffUtil是一个工具类,当你的RecyclerView需要更新数据时,将新旧数据集传给它,它就能快速告知adapter有哪些数据需要更新。就相当于如果改变了就对某个item刷新,没改变就没刷新,可以简称为局部刷新。

DiffUtil 的优势

我在最初接触 DiffUtil 时, 心中便对它有颇多的好感, 包括:

算法听提来就很nb, 一定是个好东西;
简化了 RecyclerView 的刷新逻辑, 无须关心该调用 notifyItemInserted 还是 notifyItemChanged, 一律submitList 就完事了(虽然 notifyDataSetChanged 也能做到, 但是性能拉胯, 而且没有动画);
LiveData 或者 Flow 监听单一 List 数据源时, 往往很难知道, 整个 List 中到底哪些数据项被更新了, 只能调用notifyDataSetChanged 方法, 而 DiffUtil 恰好就能解决这个问题, 无脑 submitList 就完事了.

总的来说,尽量使用diffutil,可以更好的去刷新数据,速度更快,性能更好,效果更棒。

好了不说了,上代码吧

核心类:DiffCallBack.class

import android.os.Bundle;import androidx.annotation.Nullable;
import androidx.recyclerview.widget.DiffUtil;import java.util.List;/*** cc--核心类 用来判断 新旧Item是否相等* cc 96.10.11*/
public class DiffCallBack extends DiffUtil.Callback {//旧数据集合private List<TestBean> mOldData;//新数据集合private List<TestBean> mNewData;/*** 拿到两个集合* @param mOldData* @param mNewData*/public DiffCallBack(List<TestBean> mOldData, List<TestBean> mNewData) {this.mOldData = mOldData;this.mNewData = mNewData;}/*** 旧数据集合* @return*/@Overridepublic int getOldListSize() {return mOldData != null ? mOldData.size() : 0;}/*** 新数据集合* @return*/@Overridepublic int getNewListSize() {return mNewData != null ? mNewData.size() : 0;}/*** Called by the DiffUtil to decide whether two object represent the same Item.* 被DiffUtil调用,用来判断 两个对象是否是相同的Item。* For example, if your items have unique ids, this method should check their id equality.* 例如,如果你的Item有唯一的id字段,这个方法就 判断id是否相等。* 本例判断name字段是否一致** @param oldItemPosition The position of the item in the old list* @param newItemPosition The position of the item in the new list* @return True if the two items represent the same object or false if they are different.*/@Overridepublic boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {return mOldData.get(oldItemPosition).getName().equals(mNewData.get(newItemPosition).getName());}/*** Called by the DiffUtil when it wants to check whether two items have the same data.* 被DiffUtil调用,用来检查 两个item是否含有相同的数据* DiffUtil uses this information to detect if the contents of an item has changed.* DiffUtil用返回的信息(true false)来检测当前item的内容是否发生了变化* DiffUtil uses this method to check equality instead of {@link Object#equals(Object)}* DiffUtil 用这个方法替代equals方法去检查是否相等。* so that you can change its behavior depending on your UI.* 所以你可以根据你的UI去改变它的返回值* For example, if you are using DiffUtil with a* {@link androidx.recyclerview.widget.RecyclerView.Adapter RecyclerView.Adapter}, you should* return whether the items' visual representations are the same.* 例如,如果你用RecyclerView.Adapter 配合DiffUtil使用,你需要返回Item的视觉表现是否相同。* This method is called only if {@link #areItemsTheSame(int, int)} returns* {@code true} for these items.* 这个方法仅仅在areItemsTheSame()返回true时,才调用。** @param oldItemPosition The position of the item in the old list* @param newItemPosition The position of the item in the new list which replaces the*                        oldItem* @return True if the contents of the items are the same or false if they are different.*/@Overridepublic boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {TestBean beanOld = mOldData.get(oldItemPosition);TestBean beanNew = mNewData.get(newItemPosition);if (!beanOld.getContent().equals(beanNew.getContent())) {return false;//如果有内容不同,就返回false}if (beanOld.getPic() != beanNew.getPic()) {return false;//如果有内容不同,就返回false}if (beanOld.getLookNumber() != beanNew.getLookNumber()) {return false;//如果有内容不同,就返回false}if (beanOld.getCommentNumber() != beanNew.getCommentNumber()) {return false;//如果有内容不同,就返回false}return true; //默认两个data内容是相同的}/*** When {@link #areItemsTheSame(int, int)} returns {@code true} for two items and* {@link #areContentsTheSame(int, int)} returns false for them, DiffUtil* calls this method to get a payload about the change.* <p>* 当{@link #areItemsTheSame(int, int)} 返回true,且{@link #areContentsTheSame(int, int)} 返回false时,DiffUtils会回调此方法,* 去得到这个Item(有哪些)改变的payload。* <p>* For example, if you are using DiffUtil with {@link androidx.recyclerview.widget.RecyclerView}, you can return the* particular field that changed in the item and your* {@link androidx.recyclerview.widget.RecyclerView.ItemAnimator ItemAnimator} can use that* information to run the correct animation.* <p>* 例如,如果你用RecyclerView配合DiffUtils,你可以返回  这个Item改变的那些字段,* {@link androidx.recyclerview.widget.RecyclerView.ItemAnimator ItemAnimator} 可以用那些信息去执行正确的动画* <p>* Default implementation returns {@code null}.\* 默认的实现是返回null** @param oldItemPosition The position of the item in the old list* @param newItemPosition The position of the item in the new list* @return A payload object that represents the change between the two items.* 返回 一个 代表着新老item的改变内容的 payload对象,*/@Nullable@Overridepublic Object getChangePayload(int oldItemPosition, int newItemPosition) {//实现这个方法 就能成为文艺青年中的文艺青年// 定向刷新中的部分更新// 效率最高//只是没有了ItemChange的白光一闪动画,(反正我也觉得不太重要)TestBean oldBean = mOldData.get(oldItemPosition);TestBean newBean = mNewData.get(newItemPosition);//这里就不用比较核心字段了,一定相等Bundle payload = new Bundle();if (!oldBean.getContent().equals(newBean.getContent())) {payload.putString("KEY_DESC", newBean.getContent());}if (oldBean.getPic() != newBean.getPic()) {payload.putInt("KEY_PIC", newBean.getPic());}if (oldBean.getLookNumber() != newBean.getLookNumber()) {payload.putInt("KEY_LOOK_NUMBER", newBean.getLookNumber());}if (oldBean.getCommentNumber() != newBean.getCommentNumber()) {payload.putInt("KEY_COMMENT_NUMBER", newBean.getCommentNumber());}if (payload.size() == 0)//如果没有变化 就传空return null;return payload;//}
}

DiffAdapter.class

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;import androidx.recyclerview.widget.RecyclerView;import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.drawable.DrawableTransitionOptions;
import com.bumptech.glide.request.RequestOptions;
import com.mcxtzhang.diffutils.R;import java.util.List;/*** 介绍:普普通的adapter,* 但是 唯一亮点~* public void onBindViewHolder(DiffVH holder, int position, List<Object> payloads)* 重写这个方法* cc--* cc 96.10.11*/
public class DiffAdapter extends RecyclerView.Adapter<DiffAdapter.DiffVH> {private final static String TAG = "zxt";private List<TestBean> mDatas;private Context mContext;private LayoutInflater mInflater;public DiffAdapter(Context mContext, List<TestBean> mDatas) {this.mContext = mContext;this.mDatas = mDatas;mInflater = LayoutInflater.from(mContext);}public void setDatas(List<TestBean> mDatas) {this.mDatas = mDatas;}@Overridepublic DiffVH onCreateViewHolder(ViewGroup parent, int viewType) {return new DiffVH(mInflater.inflate(R.layout.item_diff, parent, false));}@Overridepublic void onBindViewHolder(final DiffVH holder, final int position) {TestBean bean = mDatas.get(position);holder.mTvName.setText(bean.getName());holder.mTvContent.setText(bean.getContent());displayCircleImage(mContext, bean.getPic(), holder.mImgUrl, 0);holder.mTvLookNumber.setText(new StringBuffer("浏览量:").append(bean.getLookNumber()));holder.mTvCommentNumber.setText(new StringBuffer("评论量:").append(bean.getCommentNumber()));}@Overridepublic void onBindViewHolder(DiffVH holder, int position, List<Object> payloads) {if (payloads.isEmpty()) {onBindViewHolder(holder, position);} else {//文艺青年中的文青Bundle payload = (Bundle) payloads.get(0);//取出我们在getChangePayload()方法返回的bundleTestBean bean = mDatas.get(position);//取出新数据源,(可以不用)for (String key : payload.keySet()) {switch (key) {case "KEY_DESC"://这里可以用payload里的数据,不过data也是新的 也可以用holder.mTvContent.setText(bean.getContent());break;case "KEY_PIC":displayCircleImage(mContext, payload.getInt(key), holder.mImgUrl, 0);break;case "KEY_LOOK_NUMBER":holder.mTvLookNumber.setText(new StringBuffer("浏览量:").append(payload.getInt(key)));break;case "KEY_COMMENT_NUMBER":holder.mTvCommentNumber.setText(new StringBuffer("评论量:").append(payload.getInt(key)));break;default:break;}}}}@Overridepublic int getItemCount() {return mDatas != null ? mDatas.size() : 0;}class DiffVH extends RecyclerView.ViewHolder {ImageView mImgUrl;TextView mTvName, mTvContent, mTvLookNumber, mTvCommentNumber;public DiffVH(View itemView) {super(itemView);mImgUrl = itemView.findViewById(R.id.img_url);mTvName = itemView.findViewById(R.id.txt_name);mTvContent = itemView.findViewById(R.id.txt_content);mTvLookNumber = itemView.findViewById(R.id.tv_look_number);mTvCommentNumber = itemView.findViewById(R.id.tv_comment_number);}}public static void displayCircleImage(Context context, int url, ImageView imageView, int placeHolderId) {RequestOptions options = (new RequestOptions()).circleCrop().placeholder(placeHolderId);Glide.with(context).load(url).transition(DrawableTransitionOptions.withCrossFade()).apply(options).into(imageView);}}

TestBean.class

/*** 介绍:一个普通的JavaBean,但是实现了clone方法,仅仅用于写Demo时,模拟刷新从网络获取数据用,* 因为使用DiffUtils比较新老数据集差异时,会遍历新老数据集的每个data,要确保他们的内存地址(指针)不一样,否则比较的是新老data是同一个,就一定相同,* 实际项目不需要,因为刷新时,数据一般从网络拉取,并且用Gson等解析出来,内存地址一定是不一样的。* cc--* cc 96.10.11*/
public class TestBean implements Cloneable {private String name;private String content;private int pic;private int lookNumber;private int commentNumber;public TestBean(String name, String content, int pic, int lookNumber, int commentNumber) {this.name = name;this.content = content;this.pic = pic;this.lookNumber = lookNumber;this.commentNumber = commentNumber;}public int getLookNumber() {return lookNumber;}public void setLookNumber(int lookNumber) {this.lookNumber = lookNumber;}public int getCommentNumber() {return commentNumber;}public void setCommentNumber(int commentNumber) {this.commentNumber = commentNumber;}public int getPic() {return pic;}public void setPic(int pic) {this.pic = pic;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getContent() {return content;}public void setContent(String content) {this.content = content;}//仅写DEMO 用 实现克隆方法@Overridepublic TestBean clone() throws CloneNotSupportedException {TestBean bean = null;try {bean = (TestBean) super.clone();} catch (CloneNotSupportedException e) {e.printStackTrace();}return bean;}
}

MainActivity.class

import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.DiffUtil;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;import com.mcxtzhang.diffutils.R;import java.util.ArrayList;
import java.util.List;public class MainActivity extends AppCompatActivity {private List<TestBean> mDatas;private RecyclerView mRv;private DiffAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);initData();mRv = (RecyclerView) findViewById(R.id.rv);mRv.setLayoutManager(new LinearLayoutManager(this));mAdapter = new DiffAdapter(this, mDatas);mRv.setAdapter(mAdapter);}private void initData() {mDatas = new ArrayList<>();mDatas.add(new TestBean("吴大哥", "我的内容是一样的。。。", R.drawable.pic1, 34456, 865));mDatas.add(new TestBean("吴大哥", "我的内容是一样的。。。", R.drawable.pic2, 2345, 98765));mDatas.add(new TestBean("吴大哥", "我的内容是一样的。。。", R.drawable.pic3, 87, 45));mDatas.add(new TestBean("吴大哥", "我的内容是一样的。。。", R.drawable.pic5, 2368435, 7587));}/*** 模拟刷新操作** @param view*/public void onRefresh(View view) {try {mNewDatas = new ArrayList<>();for (TestBean bean : mDatas) {mNewDatas.add(bean.clone());//clone一遍旧数据 ,模拟刷新操作}mNewDatas.get(0).setLookNumber((int) (Math.random() * 200));mNewDatas.get(0).setCommentNumber((int) (Math.random() * 200));mNewDatas.get(1).setLookNumber((int) (Math.random() * 200));mNewDatas.get(1).setCommentNumber((int) (Math.random() * 200));mNewDatas.get(2).setLookNumber((int) (Math.random() * 200));mNewDatas.get(2).setCommentNumber((int) (Math.random() * 200));mNewDatas.get(3).setLookNumber((int) (Math.random() * 200));mNewDatas.get(3).setCommentNumber((int) (Math.random() * 200));//新宠//利用DiffUtil.calculateDiff()方法,传入一个规则DiffUtil.Callback对象,和是否检测移动item的 boolean变量,得到DiffUtil.DiffResult 的对象new Thread(new Runnable() {@Overridepublic void run() {//放在子线程中计算DiffResultDiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DiffCallBack(mDatas, mNewDatas), true);Message message = mHandler.obtainMessage(1);message.obj = diffResult;//obj存放DiffResultmessage.sendToTarget();}}).start();//mAdapter.notifyDataSetChanged();//以前普通青年的我们只能这样,现在我们是文艺青年了,有新宠了} catch (CloneNotSupportedException e) {e.printStackTrace();}}private List<TestBean> mNewDatas;//增加一个变量暂存newList@SuppressLint("HandlerLeak")private Handler mHandler = new Handler() {@SuppressLint("HandlerLeak")@Overridepublic void handleMessage(Message msg) {switch (msg.what) {case 1://取出ResultDiffUtil.DiffResult diffResult = (DiffUtil.DiffResult) msg.obj;//利用DiffUtil.DiffResult对象的dispatchUpdatesTo()方法,传入RecyclerView的Adapter,轻松成为文艺青年diffResult.dispatchUpdatesTo(mAdapter);//别忘了将新数据给AdaptermDatas = mNewDatas;mAdapter.setDatas(mDatas);break;}}};}

item_diff.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="wrap_content"xmlns:app="http://schemas.android.com/apk/res-auto"android:padding="20dp"android:background="@color/colorAccent"android:orientation="vertical"><ImageViewandroid:id="@+id/img_url"tools:src="@mipmap/ic_launcher"android:layout_width="50dp"android:layout_height="50dp"android:scaleType="centerCrop"tools:ignore="MissingConstraints" /><TextViewandroid:id="@+id/txt_name"android:layout_width="wrap_content"android:layout_height="wrap_content"app:layout_constraintStart_toEndOf="@+id/img_url"android:layout_marginStart="10dp"android:textColor="#ffffff"tools:text="第一个"tools:ignore="MissingConstraints" /><TextViewandroid:id="@+id/txt_content"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="10dp"android:textColor="#ffffff"app:layout_constraintTop_toBottomOf="@+id/img_url"tools:text="我的存在只为了证明定向刷新中的定向刷新"tools:ignore="MissingConstraints" /><TextViewandroid:id="@+id/tv_look_number"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="8dp"tools:text="阅读量:100"android:padding="2dp"android:textColor="#ffffff"app:layout_constraintTop_toBottomOf="@+id/txt_content"tools:ignore="MissingConstraints" /><TextViewandroid:id="@+id/tv_comment_number"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="8dp"tools:text="评论量:50"android:padding="2dp"android:textColor="#ffffff"android:layout_marginStart="30dp"app:layout_constraintTop_toBottomOf="@+id/txt_content"app:layout_constraintStart_toEndOf="@+id/tv_look_number"tools:ignore="MissingConstraints" /></androidx.constraintlayout.widget.ConstraintLayout>
注:至此,所有的代码开发工作都已经结束了。

附上demo源码。

源码:源码请点这里

如果下不了源码,可以加微信,手机号在下面。


Q:486789970(QQ现在很少用)
V:18588400509(如果着急,可以直接加微信)
email:mr.cai_cai@foxmail.com
扫码加入微信群:在这里插入图片描述

如果有什么问题,欢迎大家指导。并相互联系,希望能够通过文章互相学习。

											                               	---财财亲笔

这篇关于android-RecyclerView的DiffUtil差异化工具使用的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/561163

相关文章

中文分词jieba库的使用与实景应用(一)

知识星球:https://articles.zsxq.com/id_fxvgc803qmr2.html 目录 一.定义: 精确模式(默认模式): 全模式: 搜索引擎模式: paddle 模式(基于深度学习的分词模式): 二 自定义词典 三.文本解析   调整词出现的频率 四. 关键词提取 A. 基于TF-IDF算法的关键词提取 B. 基于TextRank算法的关键词提取

使用SecondaryNameNode恢复NameNode的数据

1)需求: NameNode进程挂了并且存储的数据也丢失了,如何恢复NameNode 此种方式恢复的数据可能存在小部分数据的丢失。 2)故障模拟 (1)kill -9 NameNode进程 [lytfly@hadoop102 current]$ kill -9 19886 (2)删除NameNode存储的数据(/opt/module/hadoop-3.1.4/data/tmp/dfs/na

Hadoop数据压缩使用介绍

一、压缩原则 (1)运算密集型的Job,少用压缩 (2)IO密集型的Job,多用压缩 二、压缩算法比较 三、压缩位置选择 四、压缩参数配置 1)为了支持多种压缩/解压缩算法,Hadoop引入了编码/解码器 2)要在Hadoop中启用压缩,可以配置如下参数

Makefile简明使用教程

文章目录 规则makefile文件的基本语法:加在命令前的特殊符号:.PHONY伪目标: Makefilev1 直观写法v2 加上中间过程v3 伪目标v4 变量 make 选项-f-n-C Make 是一种流行的构建工具,常用于将源代码转换成可执行文件或者其他形式的输出文件(如库文件、文档等)。Make 可以自动化地执行编译、链接等一系列操作。 规则 makefile文件

使用opencv优化图片(画面变清晰)

文章目录 需求影响照片清晰度的因素 实现降噪测试代码 锐化空间锐化Unsharp Masking频率域锐化对比测试 对比度增强常用算法对比测试 需求 对图像进行优化,使其看起来更清晰,同时保持尺寸不变,通常涉及到图像处理技术如锐化、降噪、对比度增强等 影响照片清晰度的因素 影响照片清晰度的因素有很多,主要可以从以下几个方面来分析 1. 拍摄设备 相机传感器:相机传

高效录音转文字:2024年四大工具精选!

在快节奏的工作生活中,能够快速将录音转换成文字是一项非常实用的能力。特别是在需要记录会议纪要、讲座内容或者是采访素材的时候,一款优秀的在线录音转文字工具能派上大用场。以下推荐几个好用的录音转文字工具! 365在线转文字 直达链接:https://www.pdf365.cn/ 365在线转文字是一款提供在线录音转文字服务的工具,它以其高效、便捷的特点受到用户的青睐。用户无需下载安装任何软件,只

Android实现任意版本设置默认的锁屏壁纸和桌面壁纸(两张壁纸可不一致)

客户有些需求需要设置默认壁纸和锁屏壁纸  在默认情况下 这两个壁纸是相同的  如果需要默认的锁屏壁纸和桌面壁纸不一样 需要额外修改 Android13实现 替换默认桌面壁纸: 将图片文件替换frameworks/base/core/res/res/drawable-nodpi/default_wallpaper.*  (注意不能是bmp格式) 替换默认锁屏壁纸: 将图片资源放入vendo

pdfmake生成pdf的使用

实际项目中有时会有根据填写的表单数据或者其他格式的数据,将数据自动填充到pdf文件中根据固定模板生成pdf文件的需求 文章目录 利用pdfmake生成pdf文件1.下载安装pdfmake第三方包2.封装生成pdf文件的共用配置3.生成pdf文件的文件模板内容4.调用方法生成pdf 利用pdfmake生成pdf文件 1.下载安装pdfmake第三方包 npm i pdfma

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

Android平台播放RTSP流的几种方案探究(VLC VS ExoPlayer VS SmartPlayer)

技术背景 好多开发者需要遴选Android平台RTSP直播播放器的时候,不知道如何选的好,本文针对常用的方案,做个大概的说明: 1. 使用VLC for Android VLC Media Player(VLC多媒体播放器),最初命名为VideoLAN客户端,是VideoLAN品牌产品,是VideoLAN计划的多媒体播放器。它支持众多音频与视频解码器及文件格式,并支持DVD影音光盘,VCD影