Урок 4 из 8 · 25 мин · обновлено
Экран на Jetpack Compose: текст, кнопки и списки
Как устроены composable-функции, как раскладывать элементы через Column и Row, настраивать их модификаторами, показывать длинные списки через LazyColumn и смотреть экран в Preview.
Интерфейс как функции
В Jetpack Compose экран описывают кодом. Каждый кусок интерфейса — composable-функция с пометкой @Composable:
@Composable
fun ProductRow(name: String, count: Int) {
Text(text = "$name — $count шт.")
}
Text — готовая composable-функция из библиотеки Material 3. Есть и другие: Button, TextField, Image, Checkbox, Card, Icon. Свои функции собирают из них, как из кирпичиков, а экран целиком — из своих функций.
Названия composable-функций пишут с заглавной буквы, как названия классов: так их легко отличить от обычных функций.
Column, Row и Box
Элементы раскладывают контейнерами:
- Column — друг под другом;
- Row — в ряд слева направо;
- Box — друг поверх друга, например значок поверх картинки.
@Composable
fun ProductRow(name: String, count: Int, onDelete: () -> Unit) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
) {
Column(modifier = Modifier.weight(1f)) {
Text(text = name, style = MaterialTheme.typography.titleMedium)
Text(text = "$count шт.", style = MaterialTheme.typography.bodySmall)
}
Button(onClick = onDelete) {
Text("Удалить")
}
}
}
Строка товара: слева колонка из названия и количества, справа кнопка.
onDelete: () -> Unit— параметр-функция: что делать при нажатии. Строка сама не решает, как удалять товар, — это решит экран, который её использует;Button(onClick = …)— кнопка, а внутри неё в фигурных скобках — её содержимое, текст;MaterialTheme.typography— готовые стили текста из темы приложения.
Modifier
Модификаторы настраивают элемент: размер, отступы, фон, рамку, реакцию на нажатия. Их пишут цепочкой, и порядок важен:
fillMaxWidth()— растянуть на всю ширину;padding(16.dp)— отступ 16 dp;weight(1f)в Row — занять всё свободное место, оттеснив кнопку вправо;background(Color.LightGray)— фон;clickable { … }— сделать элемент нажимаемым.
dp — единица размера, которая выглядит одинаково на экранах с разной плотностью пикселей. Для шрифтов используют похожую единицу sp: она учитывает размер шрифта, выбранный в настройках телефона.
Списки и Preview
Список покупок может быть длинным. Для таких списков есть LazyColumn: он создаёт только строки, видимые на экране, и подгружает остальные при прокрутке.
@Composable
fun ShoppingList(products: List<Product>) {
LazyColumn {
items(products) { product ->
ProductRow(
name = product.name,
count = product.count,
onDelete = { },
)
}
}
}
@Preview(showBackground = true)
@Composable
fun ShoppingListPreview() {
ShoppingList(
listOf(Product("Молоко", 2), Product("Хлеб", 1), Product("Яблоки", 6))
)
}
items(products) { product -> … }— для каждого товара нарисовать строку;onDelete = { }— пока кнопка ничего не делает, удаление добавим в следующем уроке;- @Preview — Android Studio покажет этот экран в панели справа, без запуска эмулятора. Удобно подбирать отступы и стили.
Попробуйте сами. Добавьте над списком заголовок «Список покупок» крупным шрифтом, а под ним — строку «Всего позиций: 3». Используйте Column, внутри которого заголовок, строка с количеством и LazyColumn.
Решение
@Composable
fun ShoppingScreen(products: List<Product>) {
Column(modifier = Modifier.padding(16.dp)) {
Text(
text = "Список покупок",
style = MaterialTheme.typography.headlineMedium,
)
Text(text = "Всего позиций: ${products.size}")
Spacer(modifier = Modifier.height(8.dp))
LazyColumn {
items(products) { product ->
ProductRow(
name = product.name,
count = product.count,
onDelete = { },
)
}
}
}
}
Spacer — пустое место нужного размера, чтобы отделить список от заголовков. Количество позиций берётся из products.size: если товаров станет больше, строка обновится сама.
Мини-тест: проверьте себя
Закрепите урок: несколько вопросов, разбор ответов — в конце.
-
Что означает пометка @Composable над функцией?
Composable-функция описывает, как выглядит кусок экрана. Из таких функций, как из кирпичиков, собирается весь интерфейс.
-
Нужно поставить название товара и кнопку «Удалить» в одну строку. Какой контейнер выбрать?
Row располагает элементы по горизонтали. Column — по вертикали, друг под другом.
-
Почему список из тысячи товаров показывают через LazyColumn, а не через Column?
Column нарисовал бы тысячу строк сразу и тормозил. LazyColumn создаёт строки по мере прокрутки и переиспользует память.
-
Как задать элементу внутренний отступ 16dp?
Внешний вид и поведение элемента в Compose настраивают модификаторами. dp — единица размера, которая одинаково выглядит на экранах с разной плотностью.