← Назад к вопросам

Какие знаешь способы отображения списка в Compose?

2.0 Middle🔥 251 комментариев
#UI и вёрстка#Архитектура и паттерны

Комментарии (1)

🐱
deepseek-v3.2PrepBro AI6 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Способы отображения списка в Jetpack Compose

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

1. Column/Row с модификатором verticalScroll/verticalScroll (для простых статических списков)

Для простых, ограниченных по размеру списков можно использовать обычный Column или Row, добавив модификатор .verticalScroll() или .horizontalScroll(). Этот способ подходит, когда количество элементов небольшое и известно заранее.

@Composable
fun SimpleList(items: List<String>) {
    Column(
        modifier = Modifier.verticalScroll(rememberScrollState())
    ) {
        items.forEach { item ->
            Text(
                text = item,
                modifier = Modifier.padding(16.dp)
            )
        }
    }
}

Применение: Статические списки настроек, короткие меню (до 10-20 элементов). Недостаток: Отсутствие ленивой загрузки - все элементы компилируются и составляются сразу, что может привести к снижению производительности при большом количестве элементов.

2. LazyColumn/LazyRow (основной способ для ленивых списков)

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

Базовое использование с items():

@Composable
fun LazyListExample(items: List<User>) {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(16.dp),
        verticalArrangement = Arrangement.spacedBy(8.dp)
    ) {
        items(items) { user ->
            UserItem(user = user)
        }
    }
}

Расширенные возможности:

а) itemsIndexed() - когда нужен индекс элемента:

itemsIndexed(items) { index, user ->
    UserItem(user = user, index = index)
}

б) Отдельные элементы в DSL - для добавления заголовков, разделителей:

LazyColumn {
    item {
        Header(title = "Список пользователей")
    }
    
    items(users) { user ->
        UserItem(user = user)
        Divider()
    }
    
    item {
        Footer(showLoadMore = true)
    }
}

в) Группировка с stickyHeader() (для прилипающих заголовков):

LazyColumn {
    users.groupBy { it.department }.forEach { (department, deptUsers) ->
        stickyHeader {
            DepartmentHeader(department)
        }
        items(deptUsers) { user ->
            UserItem(user = user)
        }
    }
}

г) Ключи элементов - для стабильной идентификации и оптимизации:

items(
    items = users,
    key = { user -> user.id } // Уникальный ключ для каждого элемента
) { user ->
    UserItem(user = user)
}

3. LazyVertical/LazyHorizontalGrid (для сеток)

Для отображения элементов в виде сетки используются компоненты LazyVerticalGrid и LazyHorizontalGrid:

@Composable
fun GridExample(products: List<Product>) {
    LazyVerticalGrid(
        columns = GridCells.Fixed(2), // Два столбца
        verticalArrangement = Arrangement.spacedBy(8.dp),
        horizontalArrangement = Arrangement.spacedBy(8.dp),
        contentPadding = PaddingValues(16.dp)
    ) {
        items(products) { product ->
            ProductCard(product = product)
        }
    }
}

Адаптивные сетки с GridCells.Adaptive(minSize):

LazyVerticalGrid(
    columns = GridCells.Adaptive(minSize = 150.dp) // Минимальная ширина ячейки
)

4. Custom Layout с LazyListScope (для сложных сценариев)

Для нестандартных расположений можно использовать LazyLayout API напрямую:

@Composable
fun CustomLazyList(items: List<Data>) {
    LazyLayout(
        modifier = Modifier.fillMaxSize(),
        prefetchState = rememberLazyLayoutPrefetchState(),
        measurePolicy = rememberCustomLayoutMeasurePolicy(items)
    )
}

Этот подход требует глубокого понимания внутренней работы Compose и используется для специфических случаев, таких как спиральные списки или сложные анимированные макеты.

5. Paging 3 с Compose (для пагинации больших наборов данных)

Для работы с очень большими списками и пагинацией данных из сети или базы данных используется библиотека Paging 3:

@Composable
fun PagingList(pagingItems: Flow<PagingData<User>>) {
    val lazyPagingItems = pagingItems.collectAsLazyPagingItems()
    
    LazyColumn {
        items(
            count = lazyPagingItems.itemCount,
            key = { index ->
                lazyPagingItems[index]?.id ?: index
            }
        ) { index ->
            val user = lazyPagingItems[index]
            if (user != null) {
                UserItem(user = user)
            } else {
                LoadingItem()
            }
        }
        
        // Индикатор загрузки
        if (lazyPagingItems.loadState.append is LoadState.Loading) {
            item {
                CircularProgressIndicator(
                    modifier = Modifier.fillMaxWidth()
                        .padding(16.dp)
                )
            }
        }
    }
}

Критерии выбора подхода:

  1. Маленькие статические спискиColumn/Row с прокруткой
  2. Динамические или длинные спискиLazyColumn/LazyRow
  3. Элементы в виде сеткиLazyVerticalGrid/LazyHorizontalGrid
  4. Очень большие наборы данных с пагинациейPaging 3 + Lazy-компоненты
  5. Нестандартные макеты → Кастомные реализации через LazyLayout

Важные аспекты производительности:

  • Ключи элементов (key параметр): Обязательны для списков с изменяемым порядком элементов
  • Стабильность типов данных: Рекомендуется использовать data class с неизменяемыми свойствами
  • Content padding и arrangement: Правильная настройка устраняет проблемы с клавиатурой и системными жестами
  • Placeholders: В Paging 3 для эффективного отображения состояния загрузки

Каждый из этих подходов обеспечивает высокую производительность и плавный пользовательский опыт, но правильный выбор зависит от конкретных требований приложения, объема данных и желаемого поведения интерфейса.

Какие знаешь способы отображения списка в Compose? | PrepBro