Перейти к основному содержимому

Kotlin — KotlinMobileApp

Разработчику Начальный уровень

О практикуме

KotlinMobileApp — учебное Android-приложение: экран с приветствием, крупным числом и кнопкой "Увеличить". Состояние счётчика сохраняется при повороте телефона через rememberSaveable.

Эталон для сверки: F:\Projects\JVM\Kotlin\KotlinMobileApp.

Теория Compose и Activity — Jetpack Compose — первый экран. Общий маршрут мобильной разработки на Kotlin — Мобильные приложения на Kotlin.

Чем отличается от XML-примера в 1135

В 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.

Карта этапов

ЭтапФокусРезультат
0Android StudioПроект с Compose и Gradle
1MainActivityActivity вызывает Compose-дерево
2ThemeMaterial3, светлая/тёмная схема
3CounterScreenВёрстка без изменяемого состояния
4СостояниеrememberSaveable, инкремент
5Ресурсы и Previewstrings.xml, @Preview

Этап 0 — проект в Android Studio

Цель этапа

Получить шаблон Android-проекта с включённым Jetpack Compose и синхронизированным Gradle.

Шаги

  1. Установите Android Studio (Ladybug или новее) — см. Kotlin в мобильных.
  2. File → New → New Project → Empty Activity (или Empty Compose Activity, если доступен).
  3. Параметры:
    • Name: KotlinMobileApp
    • Package: com.example.kotlinmobileapp
    • Language: Kotlin
    • Minimum SDK: API 26 (Android 8.0)
  4. Дождитесь 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.ktTypography (размеры заголовков и 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
Список заметок с SQLiteRoom + 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/ или нет SyncBuild → Clean Project, Sync Gradle
Кнопка не реагируетПустой onClickДобавьте { count++ }

Что попробовать

  1. Добавить кнопку "Сброс" с count = 0.
  2. Ограничить счётчик сверху (if (count < 99) count++).
  3. Вынести цвет кнопки в Color.kt и передать в Button(colors = …).
  4. Сравнить этот проект с Kotlinochi — тот же Compose, но state в ViewModel.