LazyColumn — это composable-функция в Jetpack Compose для создания вертикальных списков с ленивой загрузкой элементов. В отличие от Column, LazyColumn создаёт и компонует только видимые на экране элементы, переиспользуя их при прокрутке. По данным Google Android Documentation, 2025, LazyColumn является основным инструментом для отображения больших наборов данных в мобильных приложениях благодаря эффективному управлению памятью.
Главное
LazyColumn — это composable-функция, реализующая вертикальный прокручиваемый список с ленивой загрузкой. В отличие от Column, который отрисовывает все дочерние элементы сразу, LazyColumn создаёт только элементы, попадающие в область просмотра.
Механизм ленивой загрузки в LazyColumn основан на переиспользовании элементов: когда элемент скрывается при прокрутке, его композиция и макет могут быть переиспользованы для нового отображаемого элемента. Это экономит память и процессорное время, особенно на длинных списках.
По данным Android Developers Blog, 2024, LazyColumn может эффективно обрабатывать списки из десятков тысяч элементов без значительного снижения производительности. Каждый элемент в LazyColumn имеет ключ (key), который помогает Compose идентифицировать и сохранять состояние элемента при изменениях списка.
LazyColumn принимает параметры modifier, state, contentPadding, reverseLayout, verticalArrangement и horizontalAlignment. Содержимое задаётся в лямбда-блоке через DSL-функции items и item.
@Composable
fun SimpleLazyColumn() {
val itemsList = (1..100).toList()
LazyColumn(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(8.dp),
contentPadding = PaddingValues(16.dp)
) {
items(itemsList) { item ->
Text(text = "Item $item")
}
}
}
DSL-блок LazyColumn предоставляет функции items и item для добавления содержимого. items принимает коллекцию данных, ключ и функцию-фабрику для создания элементов. item добавляет один статический элемент в список.
itemsIndexed — это вариант items, который передаёт индекс элемента в дополнение к данным. Это полезно, когда требуется знать позицию элемента без отдельного вычисления. items(size) принимает только количество элементов и фабрику без привязки к коллекции.
Параметр key в items уникально идентифицирует каждый элемент. По данным Material Design Guidelines, 2025, ключи улучшают производительность при обновлении и анимации элементов, так как Compose может отследить перемещение или изменение конкретного элемента по ключу.
Функция items с contentType позволяет LazyColumn оптимизировать переиспользование элементов разных типов. В примере ниже в одном списке используются заголовки секций и обычные строки.
@Composable
fun MixedItemsList() {
LazyColumn {
item {
Text(
text = "Header",
style = MaterialTheme.typography.headlineMedium
)
}
items(
items = (1..20).toList(),
key = { it },
contentType = { "item" }
) { item ->
Text(text = "Row $item")
}
}
}
LazyListState — это объект состояния LazyColumn, который отслеживает текущую позицию прокрутки, первый видимый элемент и смещение. Он может быть создан через rememberLazyListState() и передан в параметр state LazyColumn.
LazyListState предоставляет методы animateScrollToItem и scrollToItem для программной прокрутки к определённому элементу. animateScrollToItem создаёт плавную анимацию прокрутки, а scrollToItem прокручивает мгновенно. Оба метода требуют вызова внутри корутины в CompositionContext.
По данным Android Developers, 2024, LazyListState также содержит layoutInfo, который даёт информацию о видимых элементах — их индексах, смещениях и размерах. Это полезно для реализации аналитики, отслеживания просмотров или бесконечной прокрутки.
В этом примере LazyColumn управляется через LazyListState. Кнопка прокручивает список к элементу с индексом 50 с плавной анимацией.
@Composable
fun ScrollableList() {
val listState = rememberLazyListState()
val scope = rememberCoroutineScope()
Column {
Button(
onClick = {
scope.launch {
listState.animateScrollToItem(50)
}
}
) {
Text("Scroll to 50")
}
LazyColumn(state = listState) {
items((1..100).toList()) { item ->
Text(
text = "Item $item",
modifier = Modifier.padding(8.dp)
)
}
}
}
}
Ключевой фактор производительности LazyColumn — правильное использование ключей. Каждый элемент должен иметь уникальный стабильный ключ через параметр key. Без ключей Compose не может корректно переиспользовать элементы при изменении данных, что приводит к перекомпоновке.
contentType сообщает LazyColumn, какие элементы имеют одинаковый тип макета. Когда два элемента с одинаковым contentType попадают в область переработки, LazyColumn может переиспользовать их макет без перекомпоновки. По данным Android Developers Blog, 2024, contentType улучшает производительность на 30–50% в списках с разными типами элементов.
Для LazyColumn также важно избегать нестабильных параметров в функциях-фабриках. Каждый раз, когда параметр элемента меняется, Compose перекомпоновывает этот элемент. Использование remember и derivedStateOf для стабильных значений снижает количество перекомпоновок.
| Фактор | Влияние | Рекомендация |
|---|---|---|
| Ключи (key) | Ключевое | Всегда задавайте уникальный стабильный key для items |
| contentType | Значительное | Указывайте тип элемента для optimizации переиспользования |
| Размер элементов | Среднее | Используйте фиксированные размеры где возможно |
| Вложенные списки | Критическое | Избегайте LazyColumn внутри LazyColumn — используйте LazyVerticalGrid |
stickyHeader — это функция DSL LazyColumn, которая закрепляет заголовок секции в верхней части списка при прокрутке. Когда следующая секция доходит до верха, её заголовок заменяет текущий. Это поведение знакомо пользователям по спискам контактов и календарям.
stickyHeader принимает ключ и функцию-фабрику, аналогично item. В отличие от обычного item, stickyHeader остаётся видимым при прокрутке содержимого своей секции. Когда секция полностью прокручена, заголовок следующей секции вытесняет предыдущий.
По данным Material Design Guidelines, 2025, sticky header улучшает навигацию в длинных списках и снижает когнитивную нагрузку на пользователя. Для реализации sticky header достаточно обернуть заголовок секции в stickyHeader в правильной позиции относительно элементов items.
@Composable
fun SectionedList(sections: Map<String, List<String>>) {
LazyColumn {
sections.forEach { (header, items) ->
stickyHeader {
Text(
text = header,
modifier = Modifier.background(Color.White)
.padding(16.dp)
)
}
items(items) { item ->
Text(text = item, modifier = Modifier.padding(8.dp))
}
}
}
}
Часто задаваемые вопросы
Column отрисовывает все элементы сразу и подходит для списков до 20–30 элементов. LazyColumn создаёт только видимые элементы и переиспользует их при прокрутке, что позволяет работать с тысячами элементов без потери производительности.
Используйте Arrangement.spacedBy в параметре verticalArrangement или функциями items с разделителем через индекс. Для сложных разделителей добавьте отдельный item с Divider() между элементами списка.
LazyColumn автоматически перекомпоновывается при изменении коллекции данных, если используются стабильные ключи. Изменения в данных, обнаруженные через mutableStateListOf или StateFlow, приводят к перерисовке только изменившихся элементов.
Прямая вложенность LazyColumn в LazyColumn не рекомендуется — обе имеют независимую прокрутку, что создаёт конфликт жестов. Вместо этого используйте обычный Column с LazyColumn внутри scrollable Section или LazyVerticalGrid для сеток.
Используйте LazyListState.layoutInfo.visibleItemsInfo и сравните индекс последнего видимого элемента с общим количеством элементов. Для бесконечной прокрутки отслеживайте, когда последний видимый элемент приближается к концу списка.
Итоги
Мы разработаем мобильное приложение под ключ
IT Sectr создаёт приложения для iOS и Android для стартапов и бизнеса с 2017 года. Мы проконсультируем вас и предложим наилучшее решение.
Читайте также