Kotlin — KotlinMobileApp
О практикуме
KotlinMobileApp — учебное Android-приложение: экран с приветствием, крупным числом и кнопкой "Увеличить". Состояние счётчика сохраняется при повороте телефона через rememberSaveable.
Эталон для сверки: F:\Projects\JVM\Kotlin\KotlinMobileApp.
Теория Compose и Activity — Jetpack Compose — первый экран. Общий маршрут мобильной разработки на Kotlin — Мобильные приложения на Kotlin.
В Kotlin в мобильных первый экран ещё на XML и findViewById. Здесь — сразу Compose: UI описывается Kotlin-функциями, без activity_main.xml.
Нужны Android Studio, JDK 17 и пройденная статья про Compose. База языка — основы Kotlin, ООП. Теория платформы — Kotlin в мобильных приложениях, маршрут — мобильные приложения на Kotlin.
Словарь практикума
| Термин | В этом проекте |
|---|---|
@Composable | Функция CounterScreen, которая рисует UI |
Scaffold | Каркас экрана с отступами под системные панели |
rememberSaveable | Хранит count в Bundle Activity при повороте |
MaterialTheme | Цвета и шрифты Material3 через KotlinMobileAppTheme |
stringResource | Текст из res/values/strings.xml, готовность к локализации |
@Preview | Превью экрана в Android Studio без эмулятора |
Архитектура проекта
app/src/main/
java/.../kotlinmobileapp/
MainActivity.kt
CounterScreen.kt
ui/theme/Color.kt, Theme.kt, Type.kt
res/values/strings.xml
Почему без ViewModel
- Одно целое число, нет фоновых задач и диска.
rememberSaveableсериализует значение в состояние Activity — для учебного счётчика этого достаточно.- Следующий шаг — Kotlinochi (ViewModel + DataStore) или Room, ViewModel и Compose — список заметок (Room).
Оценка времени — 1–2 часа. После каждого этапа нажимайте Run.
Карта этапов
| Этап | Фокус | Результат |
|---|---|---|
| 0 | Android Studio | Проект с Compose и Gradle |
| 1 | MainActivity | Activity вызывает Compose-дерево |
| 2 | Theme | Material3, светлая/тёмная схема |
| 3 | CounterScreen | Вёрстка без изменяемого состояния |
| 4 | Состояние | rememberSaveable, инкремент |
| 5 | Ресурсы и Preview | strings.xml, @Preview |
Этап 0 — проект в Android Studio
Цель этапа
Получить шаблон Android-проекта с включённым Jetpack Compose и синхронизированным Gradle.
Шаги
- Установите Android Studio (Ladybug или новее) — см. Kotlin в мобильных.
- File → New → New Project → Empty Activity (или Empty Compose Activity, если доступен).
- Параметры:
- Name:
KotlinMobileApp - Package:
com.example.kotlinmobileapp - Language: Kotlin
- Minimum SDK: API 26 (Android 8.0)
- Name:
- Дождитесь Gradle Sync (индикатор внизу IDE).
Файл app/build.gradle.kts
Проверьте ключевые фрагменты — они включают Compose и согласуют версии:
Код ITЗагрузка примера кода…
Разбор Gradle
| Строка | Зачем |
|---|---|
plugin.compose | Компилятор Compose для @Composable |
buildFeatures { compose = true } | Включает Compose в модуле app |
compose-bom | Одна версия BOM для всех артефактов androidx.compose.* |
activity-compose | Функция setContent { } в Activity |
minSdk 26 | Разумный минимум для Material3 и современных API |
| Java / JVM 17 | Требование AGP 8+ и типичный LTS JDK |
Подробнее про сборку APK — Сборка и развёртывание мобильных приложений.
Самопроверка
- Run на эмуляторе — приложение открывается.
- В Project view есть
MainActivity.ktиui/theme/. - Нет красных ошибок после Sync.
Этап 1 — MainActivity
Цель этапа
Связать жизненный цикл Android (Activity) с корнем Compose UI.
Код
Замените MainActivity.kt:
Код ITЗагрузка примера кода…
Разбор построчно
ComponentActivity— базовый класс Activity с поддержкой Compose (вместо устаревшегоAppCompatActivity+ XML).onCreate— первый вызов при создании экрана; здесь настраивают UI (жизненный цикл).enableEdgeToEdge()— контент рисуется под статус-баром и navigation bar; отступы даётScaffoldна следующих этапах.setContent { … }— заменаsetContentView(R.layout.…); внутри лямбды — только@Composable-функции.KotlinMobileAppTheme { … }— обёртка Material3 (цвета, типографика).CounterScreen()— наш экран; файл создадим на этапе 3 (пока IDE покажет unresolved reference — это нормально).
Самопроверка
- Импорты
ComponentActivity,setContentразрешаются. - Проект компилируется после добавления заглушки
CounterScreenили пропуска Run до этапа 3.
Этап 2 — тема Material3
Цель этапа
Единые цвета и шрифты для всего приложения; поддержка светлой и тёмной темы.
Теория
Material3 — дизайн-система Google для Android. В Compose тема задаётся через MaterialTheme(colorScheme, typography) — дочерние Text, Button берут стили оттуда автоматически.
Шаблон Android Studio создаёт три файла в ui/theme/:
Color.kt— константыColor(0xFF…)Type.kt—Typography(размеры заголовков и body)Theme.kt— функцияKotlinMobileAppTheme
Пример Color.kt
val Purple40 = Color(0xFF6650a4)
val Pink40 = Color(0xFF7D5260)
Пример Theme.kt
@Composable
fun KotlinMobileAppTheme(
darkTheme: Boolean = isSystemInDarkTheme(),
content: @Composable () -> Unit,
) {
val colorScheme = if (darkTheme) DarkColorScheme else LightColorScheme
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content,
)
}
Разбор
isSystemInDarkTheme()— следует системной настройке "тёмная тема".MaterialTheme(…)— провайдер стилей для всего поддерева Composable.
Самопроверка
- Любой
@PreviewсKotlinMobileAppTheme { Text("test") }показывает превью. - Переключение Dark Mode в эмуляторе меняет палитру приложения.
Этап 3 — каркас CounterScreen
Цель этапа
Сверстать экран без изменяемого состояния — только статичный текст "0" и пустой обработчик кнопки.
Теория layout в Compose
Column— дочерние элементы друг под другом.Modifier— цепочка декораций (отступы, размер);fillMaxSize(),padding().Scaffold— стандартный каркас Material;innerPaddingучитывает системные панели послеenableEdgeToEdge.Arrangement.Center— центрирование по вертикали внутри Column.
Код
Создайте CounterScreen.kt:
Код ITЗагрузка примера кода…
Разбор
| Элемент | Роль |
|---|---|
@Composable | Помечает функцию, которую Compose может вызывать при recomposition |
headlineMedium / displayLarge | Стили Material3 — крупное число визуально главнее подписи |
Spacer | Фиксированный вертикальный зазор в dp |
Button(onClick = { }) | Пока пустой lambda — кнопка не меняет состояние |
Run — на экране три блока по центру.
Самопроверка
- Текст и кнопка видны, кнопка нажимается без эффекта.
- Нет краша при повороте (состояния пока нет — это ожидаемо).
Этап 4 — состояние счётчика
Цель этапа
Сделать число изменяемым и устойчивым к повороту экрана.
Теория состояния в Compose
State — данные, от которых зависит картинка на экране. При изменении state Compose запускает recomposition — пересчитывает затронутые @Composable.
| API | Поведение |
|---|---|
remember { mutableStateOf(0) } | Живёт, пока Composable в дереве; теряется при уничтожении Activity |
rememberSaveable { mutableIntStateOf(0) } | Сохраняется в Bundle Activity — переживает поворот |
При configuration change (поворот, смена языка) Android уничтожает и создаёт Activity заново. Без rememberSaveable или ViewModel счётчик обнулится — типичная ошибка новичков (Jetpack Compose — первый экран).
Код
Код ITЗагрузка примера кода…
Разбор
var count by …— делегат: присвоениеcount++вызываетmutableIntStateOfи триггерит recomposition.mutableIntStateOf— специализированный state дляInt(меньше аллокаций, чемmutableStateOf(0)).onClick = { count++ }— lambda без параметров; каждое нажатие увеличивает state.
Проверка поворота: нажмите кнопку 5–10 раз → Ctrl+F11 (rotate) → число должно остаться тем же.
Когда появятся сеть, БД или таймер — переносите state в ViewModel (Kotlinochi, Room, ViewModel и Compose — список заметок).
Самопроверка
- Каждое нажатие увеличивает число на 1.
- После поворота эмулятора значение не сбрасывается.
Этап 5 — строковые ресурсы и Preview
Цель этапа
Вынести тексты из Kotlin в ресурсы и добавить превью для быстрой вёрстки в IDE.
Зачем strings.xml
- Локализация — позже добавите
values-en/strings.xmlбез правки кода. - Единообразие — имя приложения и подписи в одном месте.
- Практика Android — так делают production-приложения (Kotlin в мобильных приложениях).
res/values/strings.xml
<resources>
<string name="app_name">Kotlin Mobile App</string>
<string name="greeting">Привет! Это простое приложение на Kotlin</string>
<string name="increment">Увеличить</string>
</resources>
Использование в Compose
Код ITЗагрузка примера кода…
Разбор @Preview
- Компилятор генерирует отдельный preview-ран в IDE — эмулятор не нужен.
showBackground = true— белый/тёмный фон за контентом.- Preview-функция private — не попадает в APK.
Самопроверка
- Preview
CounterScreenPreviewотображается на панели Design. - Структура файлов совпадает с эталоном
KotlinMobileApp.
Что дальше
| Направление | Материал |
|---|---|
| Игра с ViewModel, DataStore, таймером | Kotlinochi |
| Список заметок с SQLite | Room + ViewModel |
| HTTP с телефона | Ktor Client |
| Сборка и USB-установка | Отладка по USB на Android |
| Публикация в Store | Публикация Android-приложения |
Частые ошибки
| Симптом | Причина | Решение |
|---|---|---|
Unresolved reference: CounterScreen | Другой package или нет файла | Положите CounterScreen.kt рядом с MainActivity |
| Счётчик сбрасывается | remember вместо rememberSaveable | Замените API (этап 4) |
| Preview пустой / ошибка | Нет MaterialTheme | Оберните в KotlinMobileAppTheme |
Unresolved reference: R | Ошибка в res/ или нет Sync | Build → Clean Project, Sync Gradle |
| Кнопка не реагирует | Пустой onClick | Добавьте { count++ } |
Что попробовать
- Добавить кнопку "Сброс" с
count = 0. - Ограничить счётчик сверху (
if (count < 99) count++). - Вынести цвет кнопки в
Color.ktи передать вButton(colors = …). - Сравнить этот проект с Kotlinochi — тот же Compose, но state в ViewModel.