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()
GeekTip #1: The recyclerview will be updated with the new set of data as a result of this.
但是,既然notifyDataSetChanged 正在为您执行工作,为什么您需要DiffUtils?让我们来谈谈它。
- 如果使用 notifyDataSetChanged() ,则 RecyclerView 无法知道真正的变化是什么。结果,所有可见视图都被重建。这是一项极其昂贵的手术。
- 在此过程中,会生成一个新的适配器实例。因此,该过程非常耗时。
为了解决这个问题,Android 引入了 DiffUtils 作为其支持库的一部分。
It’s a utility class that helps us to perform complex tasks easily, Eugene Myers’ algorithm is the foundation of DiffUtils.
DiffUtils 用于跟踪对 RecyclerView Adapter 所做的更改。 DiffUtil 使用以下方法通知 RecyclerView 数据集的任何更改:
- 通知项目已移动
- 通知项目范围已更改
- 通知项目范围插入
- 通知项目范围已移除
这些技术明显比 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
- 100 项和 10 次修改:平均:0.39 毫秒,中值:0.35 毫秒
- 100 个项目和 100 次修改为 3.82 毫秒,中位数为 3.75 毫秒。
- 2.09 ms 100 个项目和 100 次没有移动的变化,中位数为 2.06 ms。
- 1000 项和 50 次修改:平均:4.67 毫秒,中值:4.59 毫秒
- 1000 件事和 50 次变化,没有移动:平均 3.59 毫秒,平均 3.50 毫秒
- 1000 项和 200 次修改:27.07 毫秒,中位数:26.92 毫秒
- 1000 项和 200 次无移动更改:13.54 毫秒,中位数:13.36 毫秒
由于指定的限制,列表的最大大小为 226。这就是 DiffUtils 可用于更新 RecyclerView 中的列表的方式。