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)
    1. Для чего нужен адаптер?

      Отображает и обновляет данные в списке.

    1. Какие методы есть у адаптера?

      onCreateViewHolder - создаёт новый ViewHolder при необходимости.

      onBindViewHolder - связывает данные с ViewHolder на указанной позиции.

      getItemCount - возвращает общее количество элементов в наборе данных.

      getItemViewType - возвращает тип представления для указанной позиции (по умолчанию - 0).

      getItemId - возвращает уникальный идентификатор элемента на указанной позиции (если используется).

    1. Разница между RecyclerView.Adapter и ListAdapter?

      RecyclerView.Adapter требует ручного управления обновлением данных и дифференциацией элементов, что значит, что при изменении данных нужно самостоятельно обновлять список с помощью методов вроде notifyItemChanged().

      ListAdapter автоматически вычисляет разницу между старым и новым списком с помощью DiffUtil и обновляет элементы без необходимости вручного управления. Это упрощает работу с изменениями в списке.

    1. Если передать лямбду в RecyclerView.Adapter и закрыть экран будет ли утечка памяти?

      Да, если лямбда-ссылка на объект (например, Activity или Fragment) передается в RecyclerView.Adapter и не очищается, это может привести к утечке памяти. Это происходит потому, что RecyclerView.Adapter будет держать ссылку на объект, даже если экран закрыт, что мешает сборщику мусора освободить память.

    1. Какой метод используется для привязки адаптера к RecyclerView?

      setAdapter()

      linkAdapter()

      attachAdapter()

      bindAdapter()

    1. Какой метод используется для обновления содержимого AdapterView?

      notifyDataSetChanged

      updateAdapter

      refreshAdapter

      invalidateAdapter

  • DiffUtil (3)
    1. Как работает DiffUtil в RecyclerView?

      DiffUtil оптимизирует обновление списка, сравнивая старый и новый наборы данных. Вычисляет разницу между ними и сообщает RecyclerView, какие элементы нужно обновить, вставить или удалить. Вызывается метод calculateDiff, который анализирует изменения. Это повышает производительность и предотвращает мерцание при обновлениях. Под капотом следующие алгоритмы: алгоритм поиска наименьших различий и метод наибольшей общей подпоследовательности.

    1. Какие основные методы есть у DiffUtil?

      areItemsTheSame() areContentsTheSame() getChangePayload()

    1. Разница между методами areItemsTheSame и areContentsTheSame у DiffUtil?

      areItemsTheSame - проверяет, идентичны ли два элемента (по позиции) в старом и новом списках.

      areContentsTheSame - проверяет, равны ли содержимое двух элементов (по позиции) в старом и новом списках.

  • ItemDecoration (2)
    1. За что отвечает класс ItemDecorator?

      За пространство между элементами списка.

    1. С помощью какого компонента RecyclerView можно добавить разделители?

      ItemDecoration

  • Другие (4)
    1. Различие между ListView и RecyclerView?

      RecyclerView более эффективен в плане производительности, поддерживает переиспользование View, работает с LayoutManager для гибких компоновок и поддерживает анимации, в отличие от устаревшего ListView.

    1. Лагает RecyclerView в чем может быть проблема?

      Тяжелые операции в методе onBindViewHolder

      Неэффективная работа с изображениями (использовать VectorDrawable)

      Частый вызов notifyDataSetChanged

      Неправильная настройка DiffUtil

      Использовать RecycledViewPool для вложенных списков.

      Использовать setHasFixedSize setHasStableIds setItemViewCacheSize.

    1. Как обновить часть элемента списка в RecyclerView?

      В методе onBindViewHolder() можно проверить наличие payload и обновить только те части пользовательского интерфейса, которые изменились.

    1. Как RecyclerView отображает элементы?

      RecyclerView использует LayoutManager для управления компоновкой элементов. Элементы создаются и переиспользуются через Adapter и ViewHolder, что оптимизирует производительность при прокрутке. RecyclerView отрисовывает столько элементов, сколько необходимо для заполнения видимой области экрана, плюс несколько элементов вне экрана для плавной прокрутки. Точное количество зависит от размера экрана и типа LayoutManager.