DiffUtil + RecyclerView 在 Kotlin中的使用

news2024/11/25 15:41:38

很惭愧, 做了多年的Android开发还没有使用过DiffUtil这样解放双手的工具。

文章目录

  • 1 DiffUtil 用来解决什么问题?
  • 2 DiffUtil 是什么?
  • 3 DiffUtil的使用
  • 4 参考文章

1 DiffUtil 用来解决什么问题?

先举几个实际开发中的例子帮助我们感受下:

  • 加载内容流时,第一次加载了ABC,第二次加载了BCD,如何让用户能看到不重复的内容?
  • 网络数据和本地数据不一致, 如何能够找出不一致的内容?

我们可以采用最笨的方法, 自己比较两个集合的差异,但是效率较低,每个开发者都要重复做这样的事情, 是谷歌不愿意看到的。

2 DiffUtil 是什么?

DiffUtil is a utility class that calculates the difference between two lists and outputs a list of update operations that converts the first list into the second one.

It can be used to calculate updates for a RecyclerView Adapter. See ListAdapter and AsyncListDiffer which can simplify the use of DiffUtil on a background thread.

DiffUtil uses Eugene W. Myers’s difference algorithm to calculate the minimal number of updates to convert one list into another. Myers’s algorithm does not handle items that are moved so DiffUtil runs a second pass on the result to detect items that were moved.

DiffUtil 是一个实用程序类,它计算两个列表之间的差异并输出将第一个列表转换为第二个列表的更新操作列表。

它可用于计算 RecyclerView 适配器的更新。请参阅 ListAdapter 和 AsyncListDiffer,它们可以简化后台线程上 DiffUtil 的使用。

DiffUtil 使用 Eugene W. Myers 的差分算法来计算将一个列表转换为另一列表所需的最小更新次数。 Myers 的算法不处理已移动的项目,因此 DiffUtil 对结果运行第二遍以检测已移动的项目。

3 DiffUtil的使用

item_song_info.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="16dp">

    <!-- Title TextView -->
    <TextView
        android:id="@+id/tv_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Title"
        android:textSize="20sp"
        android:textStyle="bold" />

    <!-- Spacer View to add space between title and subtitle -->
    <View
        android:layout_width="8dp"
        android:layout_height="match_parent" />

    <!-- Subtitle TextView -->
    <TextView
        android:id="@+id/tv_sub_title"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Subtitle"
        android:textSize="16sp" />
</LinearLayout>

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</androidx.constraintlayout.widget.ConstraintLayout>

MusicBean.kt

data class MusicBean(var type: Int, var title: String, val subTitle: String)

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
        recyclerView.layoutManager = LinearLayoutManager(this)
        val adapter = MyAdapter()
        recyclerView.adapter = adapter

        adapter.data = getSampleDataA()

        Handler(Looper.getMainLooper()).postDelayed({
            adapter.data = getSampleDataB()
        }, 2000)
    }


    // 用于生成初始数据
    private fun getSampleDataA(): List<MusicBean> {
        val data = mutableListOf<MusicBean>()
        for (i in 1..10) {
            MusicBean(type = i, title = "ItemA $i", subTitle = "subTitle $i").let {
                data.add(it)
            }
        }
        return data
    }

    // 用于生成变化后的数据
    private fun getSampleDataB(): List<MusicBean> {
        val data = mutableListOf<MusicBean>()
        for (i in 1..10) {
            val tag = if (i <= 5) {
                "B"
            } else "A"
            MusicBean(type = i, title = "Item$tag $i", subTitle = "subTitle $i").let {
                data.add(it)
            }
        }
        return data
    }


    class MyAdapter : RecyclerView.Adapter<MyAdapter.MyViewHolder>() {

        var data: List<MusicBean> = emptyList()
            set(value) {
                // 如果比较的集合较多(比如超过1000个), 建议使用子线程去比较
                val diffResult = DiffUtil.calculateDiff(MyDiffCallback(field, value))
                // 旧值赋新值
                field = value
                // 这里一定要保证在主线程调用
                diffResult.dispatchUpdatesTo(this)
            }

        class MyDiffCallback(
            private val oldList: List<MusicBean>, private val newList: List<MusicBean>
        ) : DiffUtil.Callback() {

            override fun getOldListSize(): Int = oldList.size
            override fun getNewListSize(): Int = newList.size

            override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                // 把这里想成是比较holder的类型, 比如纯文本的holder和纯图片的holder的type肯定不同
                return oldList[oldItemPosition].type == newList[newItemPosition].type
            }

            override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
                // 把这里想成是同一种holder的比较,比如都是纯文本holder,但是title不一致
                return oldList[oldItemPosition].title == newList[newItemPosition].title
            }
        }

        override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
            val view =
                LayoutInflater.from(parent.context).inflate(R.layout.item_song_info, parent, false)
            return MyViewHolder(view)
        }

        override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
            val item = data[position]
            holder.bind(item)
        }

        override fun getItemCount(): Int {
            return data.size
        }

        class MyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {

            fun bind(item: MusicBean) {
                val tvTitle: TextView = itemView.findViewById(R.id.tv_title)
                val tvSubTitle: TextView = itemView.findViewById(R.id.tv_sub_title)
                tvTitle.text = item.title
                tvSubTitle.text = item.subTitle
            }
        }
    }
}

在这里插入图片描述

4 参考文章

DiffUtil 官方介绍
将 DiffUtil 和数据绑定与 RecyclerView 结合使用
DiffUtil和它的差量算法
DiffUtils 遇到 Kotlin,榨干视图局部刷新的最后一滴性能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1321222.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

数据分析思维导图

参考&#xff1a; https://zhuanlan.zhihu.com/p/567761684?utm_id0 1、数据分析步骤地图 2、数据分析基础知识地图 3、数据分析技术知识地图 4、数据分析业务流程 5、数据分析师能力体系 6、数据分析思路体系 7、电商数据分析核心主题 8、数据科学技能书知识地图 9、数据挖掘…

文章解读与仿真程序复现思路——电力系统自动化EI\CSCD\北大核心《基于碳捕集-电转气的矿区综合能源系统协同优化调度》

这个标题涉及到碳捕集、电力转化为气体&#xff08;可能是指电力转化为氢气等&#xff09;、矿区综合能源系统以及协同优化调度等概念。让我们逐步解读&#xff1a; 碳捕集&#xff08;Carbon Capture&#xff09;&#xff1a; 这指的是通过不同技术手段捕获和隔离工业过程中产…

输电线路定位:精确导航,确保电力传输安全

在现代社会中&#xff0c;电力作为生活的基石&#xff0c;其安全稳定运行至关重要。而输电线路作为电力传输的重要通道&#xff0c;其故障定位和修复显得尤为重要。恒峰智慧科技将为您介绍一种采用分布式行波测量技术的输电线路定位方法&#xff0c;以提高故障定位精度&#xf…

新版Android Studio Logcat 筛选日志

下载了新版的Android Studio&#xff0c;android-studio-2022.3.1.21-mac_arm&#xff0c;记录一下新版本AS的logcat过滤日志条件 1. 按照包名过滤 1.1 过滤当前包名的日志 package:mine 1.2 过滤其他包名日志 package:com.example.firstemptyapplication 2. 按照日志等级过滤…

STM32与Freertos入门(三)任务的创建、删除

1、串口配置 首先将串口进行配置&#xff0c;后续经常会应用&#xff0c;具体步骤点击&#xff1a;串口配置。 2、任务 创建一个任务&#xff0c;就是开辟一个空间、每个任务中都会有while&#xff08;1&#xff09;死循环。 2.1相关函数 动态创建&#xff1a;xTaskCreate…

Github 2023-12-19开源项目日报 Top10

根据Github Trendings的统计&#xff0c;今日(2023-12-19统计)共有10个项目上榜。根据开发语言中项目的数量&#xff0c;汇总情况如下&#xff1a; 开发语言项目数量Python项目4Rust项目2非开发语言项目2C#项目1TypeScript项目1 Avalonia: 跨平台UI框架和Avalonia XPF 创建周…

20231218给Firefly的AIO-3399J【RK3399】开发板刷Android12挖掘机方案

20231218给Firefly的AIO-3399J【RK3399】开发板刷Android12挖掘机方案 2023/12/18 21:07 一、整体编译Rockchip的的Android12的挖掘机方案&#xff01; 由于RK3399的Android12系统默认是IND工业方案&#xff0c;需要修改一下【为挖掘机方案】。 Z:\3TB\81rk_android12_220722\…

鸿蒙端H5容器化建设——JSB通信机制建设

1. 背景 2023年鸿蒙开发者大会上&#xff0c;华为宣布为了应对国外技术封锁的潜在风险&#xff0c;2024年的HarmonyOS NEXT版本中将不再兼容Android&#xff0c;并推出鸿蒙系统以及其自研的开发框架&#xff0c;形成开发生态闭环。同时&#xff0c;在更高维度上华为希望将鸿蒙…

NVMe over CXL技术如何加速Host与SSD数据传输?

在现代计算环境中&#xff0c;优化Host&#xff08;主机&#xff09;与device&#xff08;设备&#xff09;之间的数据传输速度对于提升系统整体性能和效率至关重要。目前业内主要有以下几种方案&#xff1a; Controller Memory Buffer (CMB)&#xff1a; CMB是一种集成在NVMe …

Python数据科学视频讲解:Python数据缺失值处理

3.5 Python数据缺失值处理 视频为《Python数据科学应用从入门到精通》张甜 杨维忠 清华大学出版社一书的随书赠送视频讲解3.5节内容。本书已正式出版上市&#xff0c;当当、京东、淘宝等平台热销中&#xff0c;搜索书名即可。内容涵盖数据科学应用的全流程&#xff0c;包括数据…

数据库增删改查Native SQL

DBCO&#xff1a;检查数据库是否连接 代码&#xff1a; 查询&#xff1a; DATA: gv_dbs TYPE char30 VALUE XXXXXXXX. "数据库连接名称 DATA:gt_ztclaim_2 TYPE TABLE OF ztclaim_2. DATA:gs_ztclaim_2 TYPE ztclaim_2.TRY.EXEC SQL.CONNECT TO :GV_DBSENDEXEC.EXEC SQ…

【计算机网络】TCP协议——2.连接管理(三次握手,四次挥手)

目录 前言 一. 建立连接——三次握手 1. 三次握手过程描述 2. TCP连接建立相关问题 二. 释放连接——四次挥手 1. 四次挥手过程描述 2. TCP连接释放相关问题 三. TCP状态转换 结束语 前言 TCP——传输控制协议(Transmission Control Protocol)。是一种面向连接的传…

弹幕情感分析可视化

弹幕情感分析可视化 引言1. 弹幕数据爬取2. 弹幕数据处理3. 弹幕数据可视化4. 弹幕情感分析5. 创新点&#xff1a;弹幕情感倾向分布 引言 当今互联网时代&#xff0c;大量的弹幕数据蕴含着丰富的信息&#xff0c;通过对这些数据进行分析和可视化&#xff0c;我们能够深入了解用…

【01】GeoScene生产海图或者电子航道图

1.1 什么是电子海图制图模块 GeoScene海事模块是一个用于管理和制作符合国际水文组织&#xff08;IHO&#xff09;S-100系列标准和S-57标准的海事数据的系统。提供了S-100和S-57工具&#xff0c;用于加载基于S-100的要素目录、创建基于S-57传输结构的数据、输入数据、符号化数…

U-Net 算法详解

目录 1.任务概述 2.编码器-解码器 3.跳跃连接 4.实现细节 5.损失函数 6.上采样方法 不填充还是填充&#xff1f; 7.U-Net 的运作方式 8.结论 1.任务概述 U-Net 是为语义分割任务开发的。当神经网络接受图像作为输入时&#xff0c;我们可以选择一般性地分类对象或按实例…

传统软件集成AI大模型——Function Calling

传统软件和AI大模型的胶水——Function Calling 浅谈GPT对传统软件的影响Function Calling做了什么&#xff0c;为什么选择Function CallingFunction Calling简单例子&#xff0c;如何使用使用场景 浅谈GPT对传统软件的影响 目前为止好多人对chatGPT的使用才停留在OpenAI自己提…

【Linux】sed命令使用

sed 命令 sed全称是&#xff1a;Stream EDitor。 sed 命令是利用脚本来处理文本文件。sed 一次只读取一行文本到缓冲区&#xff0c;然后读取命令&#xff0c;对此行进行编辑&#xff0c;然后读取下一行&#xff0c;重复此过程直到结束。 sed 与 vi 的区别 【Linux】 vi / v…

【DOM笔记二】操作元素(修改元素内容,修改常见元素/表单元素/元素样式属性,排他思想,自定义属性操作,应用案例!)

文章目录 4 操作元素4.1 修改元素的内容4.2 修改常见元素的属性案例&#xff1a;分时问候 4.3 修改表单元素属性案例&#xff1a;登录时隐藏/显示密码 4.4 修改元素样式属性4.4.1 行内样式操作 element.style案例1&#xff1a;关闭二维码广告案例2&#xff1a;遍历精灵图案例3&…

C# 图解教程 第5版 —— 第19章 枚举器和迭代器

文章目录 19.1 枚举器和可枚举类型19.2 IEnumerator 接口19.3 IEnumerable 接口19.4 泛型枚举接口19.5 迭代器19.5.1 迭代器块19.5.2 使用迭代器来创建枚举器19.5.3 使用迭代器来创建可枚举类型 19.6 常见迭代器模式19.7 产生多个可枚举类型19.8 将迭代器作为属性19.9 迭代器的…

Postman使用总结--参数化

将 测试数据&#xff0c;组织到 数据文件中&#xff0c;通过脚本的反复迭代&#xff0c;使用不同的数据&#xff0c;达到测试不同用例的目标 数据文件有两种&#xff1a; CSV &#xff08;类似于excel&#xff09; 格式简单用这个 文件小 JSON&#xff08;字典列表&#x…