DiffUtil в RecyclerView в Android
Вы когда-нибудь создавали список в Android? Что вы использовали, чтобы сделать это? ListView или RecyclerView представляют собой два типа представлений. Если вы являетесь разработчиком Android, вы наверняка использовали RecyclerView в какой-то момент. В этой статье мы рассмотрим, как обновить RecyclerView с помощью DiffUtils. Что такое RecyclerView? RecyclerView — это более адаптируемая и эффективная версия ListView. Это контейнер для отображения большего набора представлений данных, которые можно очень быстро перерабатывать и прокручивать. Прежде чем мы перейдем к Diff Util, давайте взглянем на реализацию RecyclerView.
Кратко о реализации RecyclerView
Давайте создадим Activity MainActivity и включим следующий код в файл main.xml активности:
XML
< androidx.constraintlayout.widget.ConstraintLayout android:layout_width = "match_parent" android:layout_height = "match_parent" tools:context = ".GeeksforGeeksActivity" > < androidx.recyclerview.widget.RecyclerView android:layout_width = "match_parent" android:layout_height = "match_parent" android:id = "@+id/gfgRecyclerView" app:layout_constraintBottom_toBottomOf = "parent" app:layout_constraintLeft_toLeftOf = "parent" app:layout_constraintRight_toRightOf = "parent" app:layout_constraintTop_toTopOf = "parent" /> </ androidx.constraintlayout.widget.ConstraintLayout > |
Давайте создадим класс модели данных и источник данных,
Kotlin
data class GeeksCourses(val courseNumber: Int, val courseRating: Int, val courseName: String) |
и источник данных, кажется,
Kotlin
object geeksforGeeks { val courseList: List<Course> get() { val course = ArrayList<Rating>() course.add(Rating( 1 , 10 , "GeeksforGeeks" )) course.add(Rating( 2 , 12 , "Android Dev" )) course.add(Rating( 3 , 5 , "DSA" )) return course } } |
Давайте теперь сделаем адаптер, чтобы установить список в RecyclerView.
Kotlin
class CourseAdapter : RecyclerView.Adapter<CourseAdapter.ViewHolder>() { private val courses = ArrayList<Course>() 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<Course>) { 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**/ ) |
и сделать вызов из MainActivity,
Kotlin
adapter.notifyDataSetChanged() |
GeekTip #1: The recyclerview will be updated with the new set of data as a result of this.
Но поскольку работу за вас выполнял notifyDataSetChanged, зачем вам понадобился DiffUtils? Давайте поговорим об этом.
- RecyclerView не может узнать, каковы реальные изменения, если используется notifyDataSetChanged(). В результате все видимые представления перестраиваются. Это чрезвычайно дорогостоящая операция.
- Во время этой процедуры создается новый экземпляр адаптера. В результате процесс занимает очень много времени.
Чтобы решить эту проблему, 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. DiffUtil уведомляет RecyclerView о любых изменениях в наборе данных, используя следующие методы:
- уведомитьItemMoved
- уведомитьItemRangeChanged
- уведомитьItemRangeInserted
- уведомитьItemRangeRemoved
Эти методы значительно более эффективны, чем notifyDataSetChanged(). Однако для того, чтобы DiffUtils функционировал в проекте, мы должны предоставить информацию о старом и новом списках. Для этого используется DiffUtil. Закажите обратный звонок. Мы сделаем класс.
Kotlin
class CoursesCallback( private val oldList: List<Course>, private val newList: List<Course>) : 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): определяет, имеют ли два объекта одинаковые данные. В зависимости от вашего пользовательского интерфейса вы можете изменить его поведение. DiffUtil вызывает эту функцию, только если areItemsTheSame возвращает значение true. В нашем примере мы противопоставляем название и цену определенного товара.
- getgeeksPayload(oldPosition:Int, newPosition:Int): если areItemTheSame возвращает true, а areContentsTheSame возвращает false, условие выполняется. Diff Этот метод вызывается Util для получения полезной нагрузки об модификации.
Чтобы использовать это, мы изменим функцию setData в адаптере.
Kotlin
fun setCourses(newCourse: List<Courses>) { val diffCallback = CoursesDiffCallback(ratings, newCourse) val diffCourses = DiffUtil.calculateDiff(diffCallback) courses.clear() courses.addAll(newCourse) diffResult.dispatchUpdatesTo( this ) } |
Каковы преимущества использования DiffUtils?
На приведенной ниже диаграмме производительности показано, что использование DiffUtil лучше в случае RecyclerView. Эти выводы основаны на Nexus 6P с M-
- 100 элементов и 10 модификаций: среднее: 0,39 миллисекунды, медиана: 0,35 миллисекунды
- 3,82 мс для 100 элементов и 100 модификаций, 3,75 мс для медианы.
- 2,09 мс для 100 элементов и 100 изменений без движений, при медиане 2,06 мс.
- 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.