📜  Android 中 RecyclerView 中的 DiffUtil

📅  最后修改于: 2022-05-13 01:54:23.692000             🧑  作者: Mango

Android 中 RecyclerView 中的 DiffUtil

您是否曾经在 Android 中创建过列表?你用什么做的? ListView 或 RecyclerView 是两种类型的视图。如果您是一名 Android 开发人员,那么您肯定曾经使用过 RecyclerView。在本文中,我们将介绍如何使用 DiffUtils 更新 RecyclerView。 RecyclerView 究竟是什么? RecyclerView 是一个适应性更强、效率更高的 ListView 版本。它是一个容器,用于显示可以非常快速地回收和滚动的更大的视图数据集。在我们进入 Diff Util 之前,让我们先看看 RecyclerView 的实现。

RecyclerView 实现简介

让我们创建一个活动 MainActivity 并在活动 main.xml 文件中包含以下代码:

XML

  
  
  


Kotlin
data class GeeksCourses(val courseNumber: Int, val courseRating: Int, val courseName: String)


Kotlin
object geeksforGeeks {
    val courseList: List
        get() {
            val course = ArrayList()
            course.add(Rating(1, 10, "GeeksforGeeks"))
            course.add(Rating(2, 12, "Android Dev"))
            course.add(Rating(3, 5, "DSA"))
            return course
        }
}


Kotlin
class CourseAdapter : RecyclerView.Adapter() {
    private val courses = ArrayList()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val view = inflater.inflate(R.layout.course_list, parent, false)
        return ViewHolder(view)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val Course = courses[position]
        holder.name_text.text = Course.name
    }
    fun setData(courses: List) {
       courses.clear()
       courses.addAll(courses)
    }
    override fun getItemCount(): Int {
        return courses.size
    }
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val name_text: TextView = itemView.findViewById(R.id.name_text)
    }
}


Kotlin
adapter.setData(/** any new data on courses**/)


Kotlin
adapter.notifyDataSetChanged()


Kotlin
class CoursesCallback(private val oldList: List, private val newList: List) : DiffUtil.Callback() {
    override fun getCourseNew(): Int = oldList.size
    override fun getNewListSize(): Int = newList.size
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].courseNumber=== newList.get(newItemPosition).rateIndex
    }
    override fun areContentsTheSame(oldCourse: Int, newPosition: Int): Boolean {
        val (_, value, name) = oldList[oldCourse]
        val (_, value1, name1) = newList[newPosition]
        return name == name1 && value == value1
    }
    @Nullable
    override fun geeksPayload(oldCourse: Int, newPosition: Int): Any? {
        return super.geeksPayload(oldCourse, newPosition)
    }
}


Kotlin
fun setCourses(newCourse: List) {
    val diffCallback = CoursesDiffCallback(ratings, newCourse)
    val diffCourses = DiffUtil.calculateDiff(diffCallback)
    courses.clear()
    courses.addAll(newCourse)
    diffResult.dispatchUpdatesTo(this)
}


现在让我们创建一个数据模型类和一个数据源,

科特林



data class GeeksCourses(val courseNumber: Int, val courseRating: Int, val courseName: String)

数据源似乎是,

科特林

object geeksforGeeks {
    val courseList: List
        get() {
            val course = ArrayList()
            course.add(Rating(1, 10, "GeeksforGeeks"))
            course.add(Rating(2, 12, "Android Dev"))
            course.add(Rating(3, 5, "DSA"))
            return course
        }
}

现在让我们制作一个适配器来设置 RecyclerView 中的列表。

科特林

class CourseAdapter : RecyclerView.Adapter() {
    private val courses = ArrayList()
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        val inflater = LayoutInflater.from(parent.context)
        val view = inflater.inflate(R.layout.course_list, parent, false)
        return ViewHolder(view)
    }
    override fun onBindViewHolder(holder: ViewHolder, position: Int) {
        val Course = courses[position]
        holder.name_text.text = Course.name
    }
    fun setData(courses: List) {
       courses.clear()
       courses.addAll(courses)
    }
    override fun getItemCount(): Int {
        return courses.size
    }
    class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val name_text: TextView = itemView.findViewById(R.id.name_text)
    }
}

如果我们需要用新信息更新列表怎么办?

我们将其称为,

科特林

adapter.setData(/** any new data on courses**/)

并从 MainActivity 调用,



科特林

adapter.notifyDataSetChanged()

但是,既然notifyDataSetChanged 正在为您执行工作,为什么您需要DiffUtils?让我们来谈谈它。

  • 如果使用 notifyDataSetChanged() ,则 RecyclerView 无法知道真正的变化是什么。结果,所有可见视图都被重建。这是一项极其昂贵的手术。
  • 在此过程中,会生成一个新的适配器实例。因此,该过程非常耗时。

为了解决这个问题,Android 引入了 DiffUtils 作为其支持库的一部分。

DiffUtils 用于跟踪对 RecyclerView Adapter 所做的更改。 DiffUtil 使用以下方法通知 RecyclerView 数据集的任何更改:

  1. 通知项目已移动
  2. 通知项目范围已更改
  3. 通知项目范围插入
  4. 通知项目范围已移除

这些技术明显比 notifyDataSetChanged() 更有效。但是,为了让 DiffUtils 在项目中函数,我们必须提供有关旧列表和新列表的信息。 DiffUtil 用于此目的。请求回电。我们要上课。

科特林

class CoursesCallback(private val oldList: List, private val newList: List) : DiffUtil.Callback() {
    override fun getCourseNew(): Int = oldList.size
    override fun getNewListSize(): Int = newList.size
    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return oldList[oldItemPosition].courseNumber=== newList.get(newItemPosition).rateIndex
    }
    override fun areContentsTheSame(oldCourse: Int, newPosition: Int): Boolean {
        val (_, value, name) = oldList[oldCourse]
        val (_, value1, name1) = newList[newPosition]
        return name == name1 && value == value1
    }
    @Nullable
    override fun geeksPayload(oldCourse: Int, newPosition: Int): Any? {
        return super.geeksPayload(oldCourse, newPosition)
    }
}

这里,

  • getOldCourse():该函数返回旧列表的长度。
  • getNewCourse():返回新列表的大小。
  • areItemsTheSame(oldPosition:Int, newPosition:Int):由 DiffUtil 调用以确定新旧列表中的两个对象是否代表相同的 Item。
  • areContentsTheSame(oldPosition:Int, newPosition:Int):判断两个对象是否有相同的数据。根据您的 UI,您可以修改其行为。 DiffUtil 仅在 areItemsTheSame 返回 true 时调用此函数。在我们的示例中,我们对比了某个商品的名称和价格。
  • 获得geeksPayload(oldPosition:Int, newPosition:Int):如果areItemTheSame返回true,areContentsTheSame返回false,则满足条件。 Diff Util 调用此方法以获取有关修改的有效负载。

为了利用这一点,我们更改了 Adapter 中的 setData函数。

科特林

fun setCourses(newCourse: List) {
    val diffCallback = CoursesDiffCallback(ratings, newCourse)
    val diffCourses = DiffUtil.calculateDiff(diffCallback)
    courses.clear()
    courses.addAll(newCourse)
    diffResult.dispatchUpdatesTo(this)
}

使用 DiffUtils 有什么好处?

下面的性能图表表明,在 RecyclerView 的情况下,使用 DiffUtil 更胜一筹。这些发现基于带有 M-的 Nexus 6P

  1. 100 项和 10 次修改:平均:0.39 毫秒,中值:0.35 毫秒
  2. 100 个项目和 100 次修改为 3.82 毫秒,中位数为 3.75 毫秒。
  3. 2.09 ms 100 个项目和 100 次没有移动的变化,中位数为 2.06 ms。
  4. 1000 项和 50 次修改:平均:4.67 毫秒,中值:4.59 毫秒
  5. 1000 件事和 50 次变化,没有移动:平均 3.59 毫秒,平均 3.50 毫秒
  6. 1000 项和 200 次修改:27.07 毫秒,中位数:26.92 毫秒
  7. 1000 项和 200 次无移动更改:13.54 毫秒,中位数:13.36 毫秒

由于指定的限制,列表的最大大小为 226。这就是 DiffUtils 可用于更新 RecyclerView 中的列表的方式。

想要一个更快节奏和更具竞争力的环境来学习 Android 的基础知识吗?
单击此处前往由我们的专家精心策划的指南,旨在让您立即做好行业准备!