RecyclerView
RecyclerView
RecyclerView — это расширяемый View
для отображения больших наборов данных, который обеспечивает высокую производительность и гибкость.
setHasFixedSize
Используется для оптимизации производительности, указывая, что размеры RecyclerView
не будут изменяться при добавлении или удалении элементов. RecyclerView
может пропустить некоторые расчеты, связанные с изменением размеров, что улучшает производительность, особенно в случаях, когда содержимое списка не влияет на размеры самого RecyclerView
. Если у вас есть фиксированное количество элементов, и их размеры не будут изменяться, вы можете установить фиксированный размер.
recyclerView.setHasFixedSize(true)
setHasStableIds
Используется в RecyclerView.Adapter
для указания, что идентификаторы элементов списка стабильны и не изменяются при изменении данных. Этот метод помогает RecyclerView
оптимизировать процесс отображения элементов. Если идентификаторы стабильны, RecyclerView
может эффективно повторно использовать представления и избегать ненужных операций по созданию и привязке. Если у вас есть список объектов, где каждый объект имеет уникальный и неизменяемый идентификатор, вы можете включить стабильные идентификаторы.
class MyAdapter: RecyclerView.Adapter<MyViewHolder>() {
init {
setHasStableIds(true)
}
override fun getItemId(position: Int): Long {
return dataList[position].id // Предполагается, что id - это уникальный идентификатор
}
// Остальные методы адаптера...
}
setNestedScrollingEnabled
Используется для включения или отключения вложенной прокрутки для View
, которая поддерживает вложенное прокручивание, например, RecyclerView
или ScrollView
. Метод позволяет управлять тем, будет ли View обрабатывать события прокрутки, происходящие из его дочерних элементов. Если у вас есть RecyclerView
внутри ScrollView
, вы можете отключить вложенную прокрутку для RecyclerView
, чтобы предотвратить конфликты прокрутки.
recyclerView.setNestedScrollingEnabled(false)
Adapter
Абстрактный класс, который связывает данные с элементами пользовательского интерфейса (ViewHolder
) в списке.
class MyAdapter(
private val items: List<String>
): RecyclerView.Adapter<MyAdapter.MyViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(itemView)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val currentItem = items[position]
holder.bind(currentItem)
}
override fun getItemCount(): Int {
return items.size
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
val textView = itemView.findViewById(R.id.textView)
fun bind(text: String) {
textView.text = text
}
}
}
recyclerView.adapter = MyAdapter(listOf("Item 1", "Item 2", "Item 3"))
onCreateViewHolder
Создаёт новый ViewHolder
, который будет использован для отображения элемента списка.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(itemView)
}
onBindViewHolder
Вызывается для привязки данных к ViewHolder
на указанной позиции. Здесь происходит обновление содержимого элемента списка в соответствии с данными.
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
val item = dataList[position]
holder.bind(item)
}
getItemCount
Возвращает количество элементов в списке. Используется для определения размера данных, которые будут отображены.
override fun getItemCount(): Int {
return dataList.size
}
getItemViewType
Возвращает тип представления для элемента на данной позиции. Может использоваться для отображения разных типов элементов в одном списке.
override fun getItemViewType(position: Int): Int {
return if (position % 2 == 0) TYPE_EVEN else TYPE_ODD
}
onViewRecycled
Вызывается, когда ViewHolder
больше не используется и может быть переработан для повторного использования. Полезно для освобождения ресурсов.
override fun onViewRecycled(holder: MyViewHolder) {
super.onViewRecycled(holder)
// Освободить ресурсы
}
onViewAttachedToWindow
Вызывается, когда ViewHolder
прикрепляется к окну, т.е. когда элемент становится видимым на экране.
override fun onViewAttachedToWindow(holder: MyViewHolder) {
super.onViewAttachedToWindow(holder)
// Действия при появлении View
}
onViewDetachedFromWindow
Вызывается, когда ViewHolder
открепляется от окна, т.е. когда элемент больше не виден.
override fun onViewDetachedFromWindow(holder: MyViewHolder) {
super.onViewDetachedFromWindow(holder)
// Действия при исчезновении View
}
ListAdapter
Cпециализированный адаптер, предназначенный для работы с коллекциями данных в RecyclerView
, оптимизирующий обновления списка при изменениях. Он наследуется от RecyclerView.Adapter
, но использует DiffUtil
для эффективного вычисления изменений в списке и их применения.
class ItemDiffCallback: DiffUtil.ItemCallback<MyItem>() {
override fun areItemsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
return oldItem.id == newItem.id
}
override fun areContentsTheSame(oldItem: MyItem, newItem: MyItem): Boolean {
return oldItem == newItem
}
}
class MyListAdapter: ListAdapter<MyItem, MyAdapter.MyViewHolder>(ItemDiffCallback()) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.item_view, parent, false)
return MyViewHolder(view)
}
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(getItem(position))
}
class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView) {
private val textView = itemView.findViewById(R.id.textView)
fun bind(item: MyItem) {
textView.text = item.name
}
}
}
submitList
Обновляет список данных. При вызове этого метода адаптер рассчитывает разницу между текущим и новым списком с помощью DiffUtil
, чтобы применить минимальные изменения.
val newList = listOf(Item(1, "One"), Item(2, "Two"))
adapter.submitList(newList)
getItem
Возвращает элемент списка на указанной позиции.
val item = adapter.getItem(0) // Item(1, "One")
getCurrentList
Возвращает текущий список элементов, который был установлен через submitList()
.
val currentList = adapter.currentList
onCreateViewHolder
Создаёт новый ViewHolder
, который будет использован для отображения элемента списка.
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
val itemView = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false)
return MyViewHolder(itemView)
}
onBindViewHolder
Связывает данные с ViewHolder
.
override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
holder.bind(getItem(position))
}
getItemViewType
Возвращает тип представления для элемента на определённой позиции.
override fun getItemViewType(position: Int): Int {
return if (getItem(position).isSpecial) 1 else 0
}
getItemCount
Возвращает количество элементов в списке.
val count = adapter.itemCount
ConcatAdapter
Объединяет несколько адаптеров в один, сохраняя при этом возможность независимого управления каждым из адаптеров.
val adapter1 = MyListAdapter(dataList1)
val adapter2 = MyListAdapter(dataList2)
val adapter3 = MyHeaderAdapter(headerData)
val concatAdapter = ConcatAdapter(adapter3, adapter1, adapter2)
recyclerView.adapter = concatAdapter
addAdapter
Добавляет новый адаптер в конец списка объединенных адаптеров.
concatAdapter.addAdapter(adapter3)
removeAdapter
Удаляет указанный адаптер из списка объединенных адаптеров.
concatAdapter.removeAdapter(adapter2)
ViewHolder
Класс, который держит ссылки на элементы интерфейса для каждого элемента списка в RecyclerView
. Он помогает оптимизировать работу, повторно используя представления (views), чтобы избежать лишних вызовов findViewById
.
class MyViewHolder(
private val binding: ItemViewBinding
): RecyclerView.ViewHolder(binding.root) {
init {
binding.textView.setOnClickListener {
println("TextView clicked at position: $adapterPosition")
}
}
fun bind(item: String) {
binding.textView.text = item
}
}
AdapterDataObserver
Используется для отслеживания изменений в адаптере данных, связанного с RecyclerView
. Он позволяет вам получать уведомления о том, когда данные в адаптере изменяются, и обновлять представление при необходимости. Является абстрактным классом, который предоставляет методы для обработки различных событий. AdapterDataObserver
полезен для управления изменениями в данных адаптера и для синхронизации состояния пользовательского интерфейса с данными, особенно в динамических списках.
class MyAdapter: RecyclerView.Adapter<MyViewHolder>() {
private val observer = object: AdapterDataObserver() {
override fun onChanged() {
super.onChanged()
// Обновление UI или другие действия
}
}
init {
registerAdapterDataObserver(observer)
}
// Другие методы адаптера...
}
override fun onCleared() {
super.onCleared()
unregisterAdapterDataObserver(observer)
}
onChanged
Вызывается, когда данные адаптера изменяются.
onItemRangeChanged
Вызывается, когда изменяются элементы в указанном диапазоне.
onItemRangeInserted
Вызывается, когда новые элементы добавляются в адаптер.
onItemRangeRemoved
Вызывается, когда элементы удаляются из адаптера.
onItemRangeMoved
Вызывается, когда элементы перемещаются внутри адаптера.
LayoutManager
Абстрактный класс, который отвечает за расположение элементов внутри RecyclerView
и определяет, как они будут отображаться, управляться и прокручиваться. Он не только позиционирует элементы, но и управляет процессом их повторного использования (recycling) и создания новых элементов.
reverseLayout
Устанавливает направление прокрутки для LayoutManager
. Если reverseLayout
равно true, элементы будут отображаться в обратном порядке.
layoutManager.reverseLayout = true // Элементы будут отображаться в обратном порядке
orientation
Определяет ориентацию LayoutManager
: вертикальную или горизонтальную.
linearLayoutManager.orientation = LinearLayoutManager.VERTICAL
gridLayoutManager.orientation = GridLayoutManager.HORIZONTAL
staggeredGridLayoutManager.orientation = StaggeredGridLayoutManager.VERTICAL
scrollToPosition
Перемещает RecyclerView
к указанной позиции, чтобы сделать элемент видимым.
recyclerView.layoutManager?.scrollToPosition(5) // Прокрутка к элементу с позицией 5
scrollToPositionWithOffset
Перемещает RecyclerView
к указанной позиции с заданным смещением. Это позволяет указать, какое расстояние от верхней или левой границы видимой области должно быть у элемента.
recyclerView.layoutManager?.scrollToPositionWithOffset(5, 20) // Прокрутка к элементу с позицией 5, смещение на 20 пикселей
findViewByPosition
Используется для получения видимого представления (View
) элемента на основе его позиции в RecyclerView
. Этот метод позволяет напрямую получить представление элемента, которое уже отображается на экране.
val view = recyclerView.layoutManager?.findViewByPosition(position)
canScrollVertically
Проверяет, может ли RecyclerView
прокручиваться вертикально. Возвращает true, если прокрутка возможна.
val canScrollVertically = recyclerView.layoutManager?.canScrollVertically() == true
canScrollHorizontally
Проверяет, может ли RecyclerView
прокручиваться горизонтально. Возвращает true, если прокрутка возможна.
val canScrollHorizontally = recyclerView.layoutManager?.canScrollHorizontally() == true
LinearLayoutManager
Управляет расположением элементов в RecyclerView
в виде линейного списка. Он позволяет организовать элементы вертикально или горизонтально.
val layoutManager = LinearLayoutManager(context)
recyclerView.layoutManager = layoutManager
setStackFromEnd
Устанавливает флаг, указывающий, следует ли располагать элементы от конца списка (например, для чатов).
layoutManager.stackFromEnd = true
findFirstVisibleItemPosition
Возвращает позицию первого видимого элемента в списке.
val firstVisiblePosition = layoutManager.findFirstVisibleItemPosition()
findLastVisibleItemPosition
Возвращает позицию последнего видимого элемента в списке.
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()
GridLayoutManager
Управляет расположением элементов в RecyclerView
в виде сетки. Он позволяет задавать количество столбцов и управлять пространством между элементами.
val layoutManager = GridLayoutManager(context, 2) // 2 столбца
recyclerView.layoutManager = layoutManager
setStackFromEnd
Устанавливает флаг, указывающий, следует ли располагать элементы от конца списка (например, для чатов).
layoutManager.stackFromEnd = true
setSpanCount
Устанавливает количество столбцов в сетке.
layoutManager.spanCount = 4
getSpanCount
Возвращает текущее количество столбцов.
val spanCount = layoutManager.spanCount
findFirstVisibleItemPosition
Возвращает позицию первого видимого элемента в списке.
val firstVisiblePosition = layoutManager.findFirstVisibleItemPosition()
findLastVisibleItemPosition
Возвращает позицию последнего видимого элемента в списке.
val lastVisiblePosition = layoutManager.findLastVisibleItemPosition()
StaggeredGridLayoutManager
Позволяет отображать элементы в виде сетки с переменной высотой. Это полезно для создания адаптивных интерфейсов, таких как сетки изображений, где каждый элемент может иметь разную высоту.
val layoutManager = StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL) // 2 столбца
recyclerView.layoutManager = layoutManager
setSpanCount
Устанавливает количество столбцов в сетке.
layoutManager.spanCount = 4
getSpanCount
Возвращает текущее количество столбцов.
val spanCount = layoutManager.spanCount
setGapStrategy
Устанавливает стратегию обработки промежутков в StaggeredGridLayoutManager
. Стратегия может влиять на то, как RecyclerView
обрабатывает видимые и невидимые элементы при изменении размера.
• GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
- позволяет автоматически перемещать элементы между спанами, чтобы заполнить пустые промежутки, которые могут возникнуть при добавлении или удалении элементов. Это помогает поддерживать более равномерное распределение элементов по всей области RecyclerView
.
• GAP_HANDLING_REMOVE_ITEMS
- устраняет промежутки между спанами, не перемещая другие элементы. При удалении элементов, пустые промежутки остаются, и другие элементы не будут изменять свои позиции.
layoutManager.gapStrategy = StaggeredGridLayoutManager.GAP_HANDLING_MOVE_ITEMS_BETWEEN_SPANS
getGapStrategy
Возвращает текущую стратегию обработки промежутков, установленную в StaggeredGridLayoutManager
.
val gapStrategy = layoutManager.getGapStrategy()
findFirstVisibleItemPositions
Находит позиции первых видимых элементов и заполняет указанный массив.
val firstVisiblePositions = IntArray(2)
layoutManager.findFirstVisibleItemPositions(firstVisiblePositions)
findLastVisibleItemPositions
Находит позиции последних видимых элементов и заполняет указанный массив.
val lastVisiblePositions = IntArray(2)
layoutManager.findLastVisibleItemPositions(lastVisiblePositions)
DiffUtil
Используется для расчета изменений между двумя списками данных и обновления RecyclerView
только тех элементов, которые изменились. Это значительно повышает производительность и эффективность работы с RecyclerView
, особенно при работе с большими объемами данных. Вместо полной перерисовки списка при обновлении данных DiffUtil
вычисляет, какие элементы были добавлены, удалены или изменены. Это позволяет избежать ненужной перерисовки и улучшает плавность анимаций.
calculateDiff
Метод, который принимает экземпляр DiffUtil.Callback
для сравнения старого и нового списков. Он возвращает DiffUtil.DiffResult
, который можно использовать для обновления RecyclerView
.
val diffResult = DiffUtil.calculateDiff(object: DiffUtil.Callback() { /* Implementation */ })
dispatchUpdatesTo
Применяет изменения к адаптеру на основе результатов, полученных от DiffUtil
.
diffResult.dispatchUpdatesTo(myRecyclerViewAdapter)
DiffUtil.Callback
Это абстрактный класс, который нужно реализовать для сравнения двух списков.
val oldList = listOf("A", "B", "C")
val newList = listOf("A", "C", "D")
val diffResult = DiffUtil.calculateDiff(object: DiffUtil.Callback() {
override fun getOldListSize(): Int = oldList.size
override fun getNewListSize(): Int = newList.size
override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
return oldList[oldItemPosition] == newList[newItemPosition]
}
})
diffResult.dispatchUpdatesTo(myRecyclerViewAdapter)
getOldListSize
Возвращает размер старого списка.
getNewListSize
Возвращает размер нового списка.
areItemsTheSame
Проверяет, ссылаются ли элементы на один и тот же объект (уникальность).
areContentsTheSame
Проверяет, имеют ли элементы одинаковые данные (содержимое).
ItemDecoration
Класс, который позволяет добавлять специальные декоративные элементы вокруг или внутри элементов списка RecyclerView
, такие как отступы, разделители, рамки и другие эффекты. Это не часть элементов, а дополнительный слой, который рисуется поверх них.
class DividerItemDecoration(
private val dividerHeight: Int,
private val dividerColor: Int
): RecyclerView.ItemDecoration() {
private val paint = Paint().apply {
color = dividerColor
style = Paint.Style.FILL
}
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
val params = child.layoutParams as RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin
val bottom = top + dividerHeight
c.drawRect(child.left.toFloat(), top.toFloat(), child.right.toFloat(), bottom.toFloat(), paint)
}
}
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
// Добавляем отступ внизу каждого элемента для разделителя
outRect.set(0, 0, 0, dividerHeight)
}
}
recyclerView.addItemDecoration(DividerItemDecoration(8, Color.GRAY))
onDraw
Вызывается для рисования фона или декораций позади элементов списка.
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
// Пример добавления разделителей между элементами
for (i in 0 until parent.childCount) {
val child = parent.getChildAt(i)
val params = child.layoutParams as RecyclerView.LayoutParams
val top = child.bottom + params.bottomMargin
val bottom = top + 2 // Толщина линии
c.drawRect(child.left.toFloat(), top.toFloat(), child.right.toFloat(), bottom.toFloat(), Paint().apply {
color = Color.GRAY
})
}
}
onDrawOver
Вызывается для рисования поверх элементов списка, после того как они уже отрисованы.
override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) {
// Например, можно добавить полупрозрачную маску поверх элементов
val paint = Paint().apply { color = Color.argb(50, 0, 0, 0) }
c.drawRect(0f, 0f, parent.width.toFloat(), parent.height.toFloat(), paint)
}
getItemOffsets
Используется для установки отступов (padding
или margin
) вокруг элементов.
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
// Пример добавления отступов между элементами
outRect.set(16, 16, 16, 16)
}
ItemAnimator
Класс, который отвечает за анимации изменений элементов в RecyclerView
. Он предоставляет возможности анимации добавления, удаления, перемещения и обновления элементов.
recyclerView.itemAnimator = DefaultItemAnimator()
DefaultItemAnimator
Стандартная реализация ItemAnimator
, используемая по умолчанию в RecyclerView
. Он обрабатывает базовые анимации для добавления, удаления и перемещения элементов в списке. Эта анимация плавная и автоматически применяется к элементам без необходимости дополнительной настройки.
val animator = DefaultItemAnimator()
animator.addDuration = 300 // Устанавливаем длительность анимации добавления
animator.removeDuration = 300 // Длительность анимации удаления
recyclerView.itemAnimator = animator
SimpleItemAnimator
Абстрактный класс, расширяющий RecyclerView.ItemAnimator
и предоставляющий базовую реализацию анимаций для элементов в RecyclerView
. В отличие от DefaultItemAnimator
, он не реализует все детали анимации, а предоставляет методы, которые разработчик может переопределить для настройки анимаций.
class CustomItemAnimator: SimpleItemAnimator() {
override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean {
// Пример простой анимации добавления
holder.itemView.alpha = 0f
holder.itemView.animate().alpha(1f).setDuration(300).start()
return true
}
override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean {
// Пример анимации удаления
holder.itemView.animate().alpha(0f).setDuration(300).start()
return true
}
override fun animateMove(holder: RecyclerView.ViewHolder, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean {
// Реализуйте анимацию перемещения элемента, если требуется
return false
}
override fun animateChange(oldHolder: RecyclerView.ViewHolder?, newHolder: RecyclerView.ViewHolder?,
fromLeft: Int, fromTop: Int, toLeft: Int, toTop: Int): Boolean {
// Пример анимации изменений
return false
}
// Переопределите эти методы, чтобы указать, когда анимации завершены
override fun runPendingAnimations() { /* Запускает ожидающие анимации */ }
override fun endAnimation(item: RecyclerView.ViewHolder) { /* Завершает анимацию элемента */ }
override fun endAnimations() { /* Завершает все анимации */ }
override fun isRunning(): Boolean { return false }
}
animateAdd
Анимация для добавления элемента в RecyclerView
.
override fun animateAdd(holder: RecyclerView.ViewHolder): Boolean {
holder.itemView.alpha = 0f
holder.itemView.animate().alpha(1f).setDuration(300).start()
return true
}
animateRemove
Анимация для удаления элемента из списка.
override fun animateRemove(holder: RecyclerView.ViewHolder): Boolean {
holder.itemView.animate().alpha(0f).setDuration(300).start()
return true
}
animateMove
Анимация перемещения элемента из одной позиции в другую.
override fun animateMove(holder: RecyclerView.ViewHolder, fromX: Int, fromY: Int, toX: Int, toY: Int): Boolean {
holder.itemView.translationX = (fromX - toX).toFloat()
holder.itemView.animate().translationX(0f).setDuration(300).start()
return true
}
animateChange
Анимация для обновления элемента (например, изменения содержимого или состояния).
override fun animateChange(oldHolder: RecyclerView.ViewHolder?, newHolder: RecyclerView.ViewHolder?,
fromLeft: Int, fromTop: Int, toLeft: Int, toTop: Int): Boolean {
oldHolder?.itemView?.animate()?.alpha(0f)?.setDuration(300)?.start()
newHolder?.itemView?.animate()?.alpha(1f)?.setDuration(300)?.start()
return true
}
isRunning
Проверяет, выполняется ли сейчас какая-либо анимация.
override fun isRunning(): Boolean {
return false // Указать, есть ли запущенные анимации
}
runPendingAnimations
Запускает все отложенные анимации.
override fun runPendingAnimations() {
// Запуск всех ожидающих анимаций
}
endAnimation
Завершает конкретную анимацию элемента.
override fun endAnimation(item: RecyclerView.ViewHolder) {
item.itemView.clearAnimation()
}
endAnimations
Завершает все текущие анимации.
override fun endAnimations() {
// Принудительное завершение всех анимаций
}
RecycledViewPool
Используется для оптимизации работы с RecyclerView
, позволяя переиспользовать ViewHolder
между несколькими RecyclerView
. Если один RecyclerView
не использует все свои ViewHolder
, они могут быть использованы другим RecyclerView
, что экономит память и время на создание. Использование RecycledViewPool
позволяет уменьшить количество создаваемых объектов при быстром скроллинге и больших списках.
val recycledViewPool = RecycledViewPool()
recyclerView1.setRecycledViewPool(recycledViewPool)
recyclerView2.setRecycledViewPool(recycledViewPool)
setMaxRecycledViews
RecycledViewPool
позволяет настроить максимальное количество ViewHolder
, которые могут быть сохранены в пуле для каждого типа представления.
recycledViewPool.setMaxRecycledViews(viewType, maxCount)
SnapHelper
Управляет автоматическим выравниванием элементов в RecyclerView
при прокрутке. Он позволяет пользователю «прилипать» к ближайшему элементу списка, создавая эффект снэпинга (snap-to), что улучшает взаимодействие и делает прокрутку более предсказуемой и удобной.
val snapHelper = MySnapHelper()
snapHelper.attachToRecyclerView(recyclerView)
attachToRecyclerView
Связывает SnapHelper
с указанным экземпляром RecyclerView
.
snapHelper.attachToRecyclerView(recyclerView)
findSnapView
Определяет, какой элемент должен быть снэпнут (приближен к центру) в зависимости от текущего положения прокрутки и заданного LayoutManager
.
val layoutManager = recyclerView.layoutManager as LinearLayoutManager
val snapView = snapHelper.findSnapView(layoutManager)
findTargetSnapPosition
Определяет позицию целевого элемента, к которому нужно «прилипнуть» на основе текущей скорости прокрутки. Возвращает позицию элемента, к которому необходимо выровнять.
override fun findTargetSnapPosition(layoutManager: RecyclerView.LayoutManager, velocityX: Int, velocityY: Int): Int {
// Возвращает целевую позицию на основе скорости прокрутки
return super.findTargetSnapPosition(layoutManager, velocityX, velocityY)
}
calculateScrollDistance
Вычисляет расстояние, которое нужно прокрутить для достижения целевого элемента. Он возвращает массив, содержащий расстояние по оси X и Y.
val distance = pagerSnapHelper.calculateScrollDistance(dx, dy)
val scrollX = distance[0]
val scrollY = distance[1]
calculateDistanceToFinalSnap
Вычисляет расстояние до окончательной позиции снэпа для указанного элемента. Возвращает массив из двух значений — расстояния по X и Y.
override fun calculateDistanceToFinalSnap(layoutManager: RecyclerView.LayoutManager, targetView: View): IntArray {
val distance = super.calculateDistanceToFinalSnap(layoutManager, targetView)
// Здесь можно использовать distance[0] и distance[1] для работы с расстоянием
return distance
}
onFling
Обрабатывает события флинга. Этот метод вызывается, когда пользователь флинет список. Если он возвращает true, флинг будет обработан, если false, то нет.
override fun onFling(recyclerView: RecyclerView, velocityX: Int, velocityY: Int): Boolean {
// Здесь можно добавить кастомное поведение при флинге
return super.onFling(recyclerView, velocityX, velocityY)
}
LinearSnapHelper
Реализация SnapHelper
, предназначенная для линейных списков, созданных с помощью RecyclerView
. Он обеспечивает автоматическое выравнивание элементов в вертикальных или горизонтальных RecyclerView
, позволяя пользователю легко «прилипать» к ближайшему элементу при прокрутке.
val linearSnapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(linearSnapHelper)
CarouselSnapHelper
Предназначен для создания эффекта карусели в RecyclerView
. Он позволяет элементам в списке «крутиться» по кругу, а также обеспечивает плавное и отзывчивое поведение при прокрутке и снэпинге.
val carouselSnapHelper = CarouselSnapHelper()
carouselSnapHelper.attachToRecyclerView(recyclerView)
PagerSnapHelper
Упрощает создание интерфейсов, аналогичных страничному прокрутке, в RecyclerView
. Он позволяет позиционировать элементы так, чтобы они «прилипали» к центру экрана при прокрутке, создавая эффект страниц, подобный тем, что можно увидеть в приложениях для чтения или в галереях изображений.
val pagerSnapHelper = PagerSnapHelper()
pagerSnapHelper.attachToRecyclerView(recyclerView)
Вопросы на собесе (15)
Adapter (6)
- Для чего нужен адаптер?
Отображает и обновляет данные в списке.
- Какие методы есть у адаптера?
•
onCreateViewHolder
- создаёт новыйViewHolder
при необходимости.•
onBindViewHolder
- связывает данные сViewHolder
на указанной позиции.•
getItemCount
- возвращает общее количество элементов в наборе данных.•
getItemViewType
- возвращает тип представления для указанной позиции (по умолчанию - 0).•
getItemId
- возвращает уникальный идентификатор элемента на указанной позиции (если используется).
- Разница между RecyclerView.Adapter и ListAdapter?
•
RecyclerView.Adapter
требует ручного управления обновлением данных и дифференциацией элементов, что значит, что при изменении данных нужно самостоятельно обновлять список с помощью методов вродеnotifyItemChanged()
.
•ListAdapter
автоматически вычисляет разницу между старым и новым списком с помощью DiffUtil и обновляет элементы без необходимости вручного управления. Это упрощает работу с изменениями в списке.
- Если передать лямбду в RecyclerView.Adapter и закрыть экран будет ли утечка памяти?
Да, если лямбда-ссылка на объект (например,
Activity
илиFragment
) передается вRecyclerView.Adapter
и не очищается, это может привести к утечке памяти. Это происходит потому, чтоRecyclerView.Adapter
будет держать ссылку на объект, даже если экран закрыт, что мешает сборщику мусора освободить память.
- Какой метод используется для привязки адаптера к RecyclerView?
•
setAdapter()
•
linkAdapter()
•
attachAdapter()
•
bindAdapter()
- Какой метод используется для обновления содержимого AdapterView?
•
notifyDataSetChanged
•
updateAdapter
•
refreshAdapter
•
invalidateAdapter
- Для чего нужен адаптер?
DiffUtil (3)
- Как работает DiffUtil в RecyclerView?
DiffUtil
оптимизирует обновление списка, сравнивая старый и новый наборы данных. Вычисляет разницу между ними и сообщаетRecyclerView
, какие элементы нужно обновить, вставить или удалить. Вызывается методcalculateDiff
, который анализирует изменения. Это повышает производительность и предотвращает мерцание при обновлениях. Под капотом следующие алгоритмы: алгоритм поиска наименьших различий и метод наибольшей общей подпоследовательности.
- Какие основные методы есть у DiffUtil?
areItemsTheSame()
areContentsTheSame()
getChangePayload()
- Разница между методами areItemsTheSame и areContentsTheSame у DiffUtil?
•
areItemsTheSame
- проверяет, идентичны ли два элемента (по позиции) в старом и новом списках.•
areContentsTheSame
- проверяет, равны ли содержимое двух элементов (по позиции) в старом и новом списках.
- Как работает DiffUtil в RecyclerView?
ItemDecoration (2)
- За что отвечает класс ItemDecorator?
За пространство между элементами списка.
- С помощью какого компонента RecyclerView можно добавить разделители?
ItemDecoration
- За что отвечает класс ItemDecorator?
Другие (4)
- Различие между ListView и RecyclerView?
RecyclerView
более эффективен в плане производительности, поддерживает переиспользованиеView
, работает сLayoutManager
для гибких компоновок и поддерживает анимации, в отличие от устаревшегоListView
.
- Лагает RecyclerView в чем может быть проблема?
• Тяжелые операции в методе
onBindViewHolder
• Неэффективная работа с изображениями (использовать
VectorDrawable
)• Частый вызов
notifyDataSetChanged
• Неправильная настройка
DiffUtil
• Использовать
RecycledViewPool
для вложенных списков.• Использовать
setHasFixedSize
setHasStableIds
setItemViewCacheSize
.
- Как обновить часть элемента списка в RecyclerView?
В методе
onBindViewHolder()
можно проверить наличие payload и обновить только те части пользовательского интерфейса, которые изменились.
- Как RecyclerView отображает элементы?
RecyclerView
используетLayoutManager
для управления компоновкой элементов. Элементы создаются и переиспользуются черезAdapter
иViewHolder
, что оптимизирует производительность при прокрутке.RecyclerView
отрисовывает столько элементов, сколько необходимо для заполнения видимой области экрана, плюс несколько элементов вне экрана для плавной прокрутки. Точное количество зависит от размера экрана и типаLayoutManager
.
- Различие между ListView и RecyclerView?