Данфа

Контейнеры верстки в Jetpack Compose

В Jetpack Compate интерфейс строится не из XML-файлов, а из функций Kotlin. Чтобы управлять положением кнопок, текстов и картинок, используются специальные контейнеры — Composable-функции, которые диктуют правила расположения своих «детей».

Разберем всю иерархию: от базовых строительных блоков до мощных инструментов для сложных экранов.

Column() — Вертикальный стек


Располагает элементы друг под другом, сверху вниз. Это прямой аналог LinearLayout(vertical).

Для чего нужен:
  • Формы регистрации и экраны настроек.
  • Ленты постов, где каждый элемент идет за другим.
  • Группировка заголовка, текста и кнопки.

Пример:
@Composable
fun UserProfile() {
    Column(
        modifier = Modifier.padding(16.dp),
        horizontalAlignment = Alignment.CenterHorizontally // Центрируем всё по ширине
    ) {
        Text(text = "Алексей", style = MaterialTheme.typography.headlineSmall)
        Text(text = "@prizrak_dev", color = Color.Gray)
        Spacer(modifier = Modifier.height(12.dp)) // Пустое пространство
        Button(onClick = { /* ... */ }) { Text("Написать") }
    }
}


Row() — Горизонтальный стек


Располагает элементы слева направо. Аналог LinearLayout(horizontal).

Для чего нужен:
  • Элементы меню (иконка + текст).
  • Панели инструментов (Toolbar).
  • Кнопки, где текст и иконка стоят в ряд.

Пример:
@Composable
fun MessageItem() {
    Row(
        modifier = Modifier
            .fillMaxWidth()
            .padding(8.dp),
        verticalAlignment = Alignment.CenterVertically // Выравниваем по центру высоты
    ) {
        Icon(Icons.Default.Email, contentDescription = null)
        Spacer(modifier = Modifier.width(8.dp)) // Отступ
        Text(text = "Новое сообщение")
    }
}


Box() — Наложение


Накладывает элементы друг на друга. Порядок важен: элементы, написанные в коде ниже, оказываются поверх предыдущих. Аналог FrameLayout.

Для чего нужен:
  • Значки уведомлений на иконках (Бейджи).
  • Градиентная подложка поверх изображения.
  • Центрирование одного элемента по центру экрана.

Пример:
@Composable
fun ProfileWithBadge() {
    Box(contentAlignment = Alignment.TopEnd) {
        // Нижний слой
        Image(painter = painterResource(id = R.drawable.avatar), 
              contentDescription = "Аватар", 
              modifier = Modifier.size(64.dp))
        
        // Верхний слой (поверх аватара)
        Box(
            modifier = Modifier
                .size(16.dp)
                .background(Color.Red, CircleShape)
                .border(2.dp, Color.White, CircleShape)
        )
    }
}


LazyColumn() и LazyRow() — Ленивые списки


Самые важные контейнеры для производительности. Они создают элементы только тогда, когда они появляются на экране.

Для чего нужен:
  • Чаты, ленты новостей, списки товаров.
  • Любые экраны с большим количеством данных.

Важно: Никогда не используйте обычный Column для списка из 50+ элементов — приложение зависнет. Используйте LazyColumn.

Пример:
@Composable
fun ChatHistory(messages: List<String>) {
    LazyColumn(
        modifier = Modifier.fillMaxSize(),
        contentPadding = PaddingValues(bottom = 70.dp) // Отступ под поле ввода
    ) {
        items(messages) { msg ->
            Text(text = msg, modifier = Modifier.padding(8.dp))
        }
    }
}


Scaffold() — Каркас Material Design


Это не просто контейнер, а готовый шаблон экрана по правилам Google Material. Он предоставляет «слоты» для стандартных элементов.

Для чего нужен:
  • Чтобы быстро собрать экран с TopAppBar, BottomAppBar и FloatingActionButton (FAB).
  • Для удобной работы с Snackbar.
  • Он автоматически учитывает «вырезы» экрана (Камера, навигационная панель).

Пример:
@Composable
fun HomeScreen() {
    Scaffold(
        topBar = { Text("Rush Messenger", Modifier.fillMaxWidth().padding(16.dp)) },
        floatingActionButton = {
            FloatingActionButton(onClick = {}) { Icon(Icons.Default.Add, null) }
        }
    ) { innerPadding -> 
        // Основной контент. Scaffold сам передает отступы, чтобы контент не ушел под FAB.
        LazyColumn(contentPadding = innerPadding) {
            items(100) { Text("Сообщение #$it") }
        }
    }
}


ConstraintLayout() — Сложные связи


Мощный инструмент для создания сложных интерфейсов без глубокой вложенности Column в Row. Позволяет привязывать границы одного элемента к границам другого.

Для чего нужен:
  • Сложные карточки товаров.
  • Экраны, где элементы должны быть выровнены относительно друг друга по диагонали или крест-накрест.
  • Когда Column/Row требуют слишком много вложенности.

Пример:
@Composable
fun ComplexCard() {
    ConstraintLayout(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
        val (title, subtitle, button) = createRefs()

        Text("Важный заголовок", Modifier.constrainAs(title) {
            top.linkTo(parent.top)
            start.linkTo(parent.start)
        })

        Text("Описание", Modifier.constrainAs(subtitle) {
            top.linkTo(title.bottom, 8.dp)
            start.linkTo(title.start)
            end.linkTo(button.start, 8.dp) // Текст не заходит на кнопку
        })

        Button(onClick = {}, Modifier.constrainAs(button) {
            end.linkTo(parent.end)
            top.linkTo(title.top)
            bottom.linkTo(title.bottom)
        }) {
            Text("Действие")
        }
    }
}


Layout() — Создание своего контейнера


Это фундамент Compose. Если вам не подходит ни один стандартный контейнер, вы можете написать свой. С помощью Layout вы вручную измеряете и расставляете дочерние элементы.

Для чего нужен:
  • Создание уникальных макетов (например, круговое меню или верстка в виде дуги).
  • Максимальная оптимизация, если стандартные контейнеры делают лишнюю работу.

Пример (Простейший Column своими руками):
@Composable
fun MyOwnColumn(content: @Composable () -> Unit) {
    Layout(content = content) { measurables, constraints ->
        val placeables = measurables.map { it.measure(constraints) }
        var height = placeables.sumOf { it.height }
        
        layout(constraints.maxWidth, height) {
            var y = 0
            placeables.forEach { placeable ->
                placeable.place(x = 0, y = y)
                y += placeable.height
            }
        }
    }
}


Итог: Когда и что использовать?


  • Column: Простой список сверху вниз.
  • Row: Панель инструментов или меню слева направо.
  • Box: Иконка поверх картинки или центрирование одного элемента.
  • LazyColumn: Чаты, ленты, каталоги (любые большие списки).
  • Scaffold: Стандартный экран приложения с панелью сверху и кнопкой FAB.
  • ConstraintLayout: Сложные связи между элементами, чтобы избежать вложенности.
  • Layout: Когда нужно создать что-то, чего нет в стандартной библиотеке.


Автор:  07.11.2026 07:20:24 pm