← Назад к вопросам
Что делать если LazyRow лагает
2.8 Senior🔥 62 комментариев
#UI и вёрстка#Производительность и оптимизация
Комментарии (2)
🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)
Ответ сгенерирован нейросетью и может содержать ошибки
Проблема лагов в LazyRow и её решение
LazyRow — это мощный инструмент Compose для горизонтальной ленивой компоновки, но при неправильном использовании он действительно может вызывать лаги, особенно при работе с большими наборами данных или сложными элементами. Давайте разберем основные причины и практические решения.
🔍 Диагностика проблемы
Первым делом нужно определить источник лагов. Используйте Layout Inspector и Compose Metrics:
// Включите отслеживание рекомпозиций в разработке
@Composable
fun MyLazyRowContent() {
LazyRow {
items(items) { item ->
KeyedComposition(item.id) { // Используйте ключи для стабильности
MyRowItem(item)
}
}
}
}
🎯 Основные причины и решения
1. Чрезмерная рекомпозиция элементов
// ❌ ПЛОХО: весь элемент перекомпозируется при любых изменениях
@Composable
fun BadListItem(item: Item) {
var localState by remember { mutableStateOf(false) }
// Сложная логика рекомпозиции
}
// ✅ ХОРОШО: разделяйте стабильные и изменяемые части
@Composable
fun GoodListItem(item: Item) {
Row {
// Статичная часть - используйте remember
Image(
painter = rememberAsyncImagePainter(item.imageUrl),
contentDescription = null,
modifier = Modifier.fillMaxHeight()
)
// Динамическая часть - изолируйте состояние
ItemDetails(item) // Выносите в отдельную composable функцию
}
}
2. Тяжелые операции в Composable функциях
// ❌ ПЛОХО: вычисления внутри composable
@Composable
fun HeavyComputationItem(data: Data) {
val computedValue = performHeavyCalculation(data) // Блокирует UI поток
Text(text = "Result: $computedValue")
}
// ✅ ХОРОШО: выносите вычисления в background
@Composable
fun OptimizedItem(data: Data) {
val computedValue by remember(data) {
derivedStateOf {
// Используйте Dispatchers.Default для тяжелых операций
withContext(Dispatchers.Default) {
performHeavyCalculation(data)
}
}
}
Text(text = "Result: $computedValue")
}
📊 Оптимизационные стратегии
Размеры и кэширование
LazyRow(
state = rememberLazyListState(),
modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = 16.dp),
flingBehavior = ScrollableDefaults.flingBehavior(),
userScrollEnabled = true
) {
items(
items = items,
key = { it.id }, // Ключи обязательны для правильной переиспользуемости
contentType = { it.type } // Группировка по типу для оптимизации
) { item ->
MyListItem(
item = item,
modifier = Modifier
.width(IntrinsicSize.Max) // Используйте intrinsic размеры
.animateItemPlacement() // Анимация переупорядочивания
)
}
}
Пагинация и предзагрузка
val lazyListState = rememberLazyListState()
LaunchedEffect(lazyListState) {
snapshotFlow { lazyListState.layoutInfo }
.map { layoutInfo ->
val lastVisible = layoutInfo.visibleItemsInfo.lastOrNull()
lastVisible?.index == layoutInfo.totalItemsCount - 5
}
.distinctUntilChanged()
.collect { needLoadMore ->
if (needLoadMore) viewModel.loadMore()
}
}
🛠️ Дополнительные техники оптимизации
- Использование
derivedStateOfдля минимизации рекомпозиций:
val scrollState = rememberLazyListState()
val isScrolling by remember {
derivedStateOf {
scrollState.isScrollInProgress
}
}
- Оптимизация изображений с помощью Coil или Glide:
AsyncImage(
model = ImageRequest.Builder(context)
.data(item.url)
.diskCachePolicy(CachePolicy.ENABLED)
.memoryCachePolicy(CachePolicy.ENABLED)
.size(Size.ORIGINAL)
.build(),
contentDescription = null,
modifier = Modifier.fillMaxSize(),
contentScale = ContentScale.Crop
)
- Виртуализация содержимого — рендерите только видимые элементы:
LazyRow(
state = rememberLazyListState(),
beyondBoundsItemCount = 2, // Рендерить +2 элемента за пределами видимости
// ...
)
🚀 Профилирование производительности
- Включите Show Layout Bounds в настройках разработчика
- Используйте Compose Compiler Metrics для анализа стабильности
- Проверяйте Skipped frames в Logcat с тегом
Choreographer - Замеряйте время рекомпозиций с помощью
BenchmarkRuleв тестах
💡 Практические советы
- Избегайте вложенных ленивых layout'ов — это убийца производительности
- Используйте
Modifierс умом — некоторые модификаторы очень дорогие - Минимизируйте количество состояний в каждом элементе
- Профилируйте на реальных устройствах, особенно среднего и низкого класса
- Рассмотрите альтернативы — для простых списков иногда лучше подойдет
Rowс горизонтальным скроллом
Помните, что оптимальная производительность достигается балансом между функциональностью и оптимизацией. Начинайте с простой реализации, добавляйте оптимизации только после выявления конкретных проблем через профилирование.