Первое приложение на Jetpack Compose
Jetpack Compose — это современный декларативный UI-тулкит, где интерфейс описывается обычными Kotlin-функциями с аннотацией Composable, а не XML-разметкой.
Суть: в Compose вы описываете, как выглядит экран при данном состоянии, а фреймворк сам обновляет картинку при изменении данных — это проще и надёжнее старого императивного подхода.
До Compose интерфейс верстали в XML, а потом в коде искали элементы по идентификаторам и вручную меняли их свойства. Это было многословно и легко рассинхронизировалось. Compose переворачивает подход: интерфейс — это функция от состояния. Вы пишете @Composable-функции, которые описывают, что показать, а при изменении данных фреймворк перерисовывает нужные части сам.
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@Composable
fun Greeting(name: String) {
Text(text = "Привет, $name!")
}Composable-функция — это обычная функция Kotlin, помеченная @Composable. Она ничего не возвращает, а вместо этого вызывает другие composable-функции, описывая дерево интерфейса. Точка входа — setContent в Activity.
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
Greeting(name = "мир")
}
}
}Базовые компоненты и контейнеры
Самые частые composable: Text — текст, Button — кнопка, Image — картинка. Для расположения используются контейнеры: Column размещает детей по вертикали, Row — по горизонтали, Box накладывает их друг на друга.
@Composable
fun WelcomeScreen() {
Column {
Text("Добро пожаловать")
Button(onClick = { /* действие */ }) {
Text("Начать")
}
}
}Как работает под капотом
Когда вы вызываете composable-функции, Compose не рисует сразу пиксели. Он строит описание интерфейса в виде дерева и сравнивает его с предыдущим. Изменилось только текстовое поле — перерисуется только оно. Этот процесс называется композицией, а повторный вызов при изменении данных — рекомпозицией (её мы подробно разберём в следующем разделе). Аннотация @Preview позволяет увидеть composable прямо в Android Studio без запуска на устройстве.
import androidx.compose.ui.tooling.preview.Preview
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
Greeting(name = "превью")
} Дерево Composable
WelcomeScreen
|
+-- Column
|
+-- Text("Добро пожаловать")
+-- Button
|
+-- Text("Начать")Частые ошибки
Забыть аннотацию Composable. Без неё функция не может вызывать другие composable и не встроится в дерево интерфейса.
Возвращать значение из composable. Composable описывает UI через вызовы, а не возвращает виджеты; ничего возвращать не нужно.
Класть всё в одну гигантскую функцию. Разбивайте экран на мелкие переиспользуемые composable — так читаемее и эффективнее перерисовка.
Best practices
- Делайте composable маленькими и сфокусированными на одной задаче.
- Снабжайте ключевые composable превью через
@Preview. - Передавайте данные параметрами, а не тяните их из глобального состояния.
- Именуйте composable существительными с заглавной буквы (Greeting, WelcomeScreen).
Идею «UI как функция от состояния» удобно прочувствовать на Python: одна функция строит описание экрана из данных. Запустите врезку.
# Аналог 'UI как функция от состояния' из Compose
def greeting(name):
return 'Text("Привет, ' + name + '!")'
def welcome_screen(user):
lines = ['Column {']
lines.append(' ' + greeting(user))
lines.append(' Button { Text("Начать") }')
lines.append('}')
return '\n'.join(lines)
print(welcome_screen('мир'))
print('---')
print(welcome_screen('Аня'))Попробуй сам ▶ — меняйте имя пользователя и смотрите, как меняется описание экрана. В Compose ровно так же: меняются данные — пересобирается описание UI.
Закрепим главное
Закрепите главный сдвиг мышления: вы больше не отдаёте команды интерфейсу («найди элемент, поменяй текст»), а описываете, как он должен выглядеть при данных. Эта разница принципиальна. В императивном UI рассинхронизация состояния и экрана была вечным источником багов; в декларативном Compose экран по определению является функцией состояния, поэтому он не может «отстать» от данных — фреймворк сам приведёт картинку в соответствие.
Второй ориентир — композиция как способ строить сложное из простого. Большой экран — это не одна огромная функция, а дерево маленьких composable, каждый из которых отвечает за свой кусочек и снабжён превью. Такая декомпозиция облегчает чтение, ускоряет точечную перерисовку и упрощает переиспользование. Привычка дробить интерфейс на мелкие осмысленные функции окупится сразу, как только мы добавим состояние и начнём поднимать его наверх по дереву.
Итог: Compose описывает интерфейс декларативно через @Composable-функции, базовые компоненты и контейнеры Column/Row/Box. Это первый шаг; дальше мы научимся делать интерфейс по-настоящему живым с помощью состояния.