Мета та потрібні знання
Створити застосунок перетворення хвилин у години й хвилини, відокремивши правило перевірки від UI. Потрібно знати val/var, nullable, composable та observable state. Демонстрація показує техніку керованого введення; основним самостійним завданням буде інший предметний розрахунок із власними правилами.
Підготовка проєкту
В Android Studio виберіть New Project → Empty Activity з Compose. Назва TimeLab, package com.example.timelab, Kotlin, minSdk 24 або вище. Залиште узгоджені Gradle, Kotlin і Compose залежності, створені шаблоном. Дочекайтеся Gradle Sync. У Device Manager створіть AVD з API не нижче minSdk й запустіть порожній проєкт. Це відділить помилку середовища від вашого коду.
Замініть app/src/main/java/com/example/timelab/MainActivity.kt повним кодом нижче. Додаткових бібліотек не потрібно: Material3 і Compose runtime вже є у шаблоні. Якщо ви вибрали інший package, змініть перший рядок відповідно до свого проєкту. Інші файли шаблону, включно з manifest launcher Activity, залишаються потрібними для запуску.
Теоретичний мінімум
Текст поля не є числом до перевірки. toIntOrNull відхиляє нечислове введення, а діапазон задає предметний контракт. rememberSaveable зберігає невеликий стан для відновлення; це не постійна база. Клавіатура Number є підказкою UI і не замінює перевірки, оскільки дані можуть бути вставлені іншими способами.
Повний демонстраційний код
package com.example.timelab
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material3.Button
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent { MaterialTheme { Surface { TimeScreen() } } }
}
}
fun parseMinutes(raw: String): Int? =
raw.trim().toIntOrNull()?.takeIf { it in 0..1440 }
@Composable
fun TimeScreen() {
var input by rememberSaveable { mutableStateOf("") }
var message by rememberSaveable { mutableStateOf("Введіть хвилини") }
Column(Modifier.fillMaxSize().padding(24.dp),
verticalArrangement = Arrangement.spacedBy(16.dp)) {
Text("Перетворення часу", style = MaterialTheme.typography.headlineMedium)
OutlinedTextField(value = input, onValueChange = { input = it },
label = { Text("Хвилини: 0–1440") }, singleLine = true,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number))
Button(onClick = {
val value = parseMinutes(input)
message = if (value == null) "Потрібне ціле число 0–1440"
else "${value / 60} год ${value % 60} хв"
}) { Text("Обчислити") }
Text(message)
}
}
Ділення дає цілу кількість годин, % — залишок хвилин. Для 125 очікується «2 год 5 хв», для 0 — «0 год 0 хв». MutableState повідомляє Compose про зміну message. By потребує getValue/setValue imports. Message оновлюється лише за подією кнопки; введення саме по собі не виконує розрахунок. Це свідомий контракт демонстрації.
Покрокове виконання
Запустіть Run на AVD. Перевірте 125, 0, 1440, 1441, −1, порожній текст і літери через вставлення. Для кожного запишіть очікування до натискання. Поверніть пристрій після правильного розрахунку й перевірте відновлення поля та повідомлення. Потім закрийте й відкрийте застосунок, пояснюючи, що саме цей запуск перевіряє щодо збереження.
Винесіть форматування у чисту функцію та перевірте її без UI в Kotlin scratch або тесті проєкту. Переконайтеся, що помилка не зберігає неправильний числовий результат. Перевірте великий шрифт: поля й кнопки повинні залишатися доступними. За потреби додайте прокручування, пояснивши його роль на маленькому екрані.
Самостійне завдання
Створіть калькулятор навчального прогресу або перетворення одиниць із власним контрактом. Потрібні два поля або додатковий режим, результат і зрозуміла помилка. Складіть щонайменше шість перевірок, включно з границями. Основну формулу, типи й дизайн стану ви маєте визначити самостійно.
Додаткові вимоги: відокремити stateless форму від state holder, додати Preview порожнього й помилкового стану та пояснити, які дані повинні пережити process death. Не використовуйте !! для введення. Не додавайте мережу або permissions, яких ця задача не потребує.
Типові помилки та налагодження
Unresolved reference часто означає пропущений import або невдалий Gradle Sync. Операція з Int може втратити дробову частину там, де потрібний Double. Поле з value без оновлення у onValueChange не дозволяє редагування. Використання звичайної var без observable holder не гарантує UI оновлення. Перевіряйте чисту функцію окремо, а UI — як відображення стану.
Результат і контрольні питання
Подайте Android source project без build/.gradle, опис контракту й таблицю перевірок. Поясніть rememberSaveable, keyboardOptions і подію розрахунку. Чому numeric keyboard не є валідацією? Що відбувається після зміни конфігурації? Який стан потрібно зберегти для вашої власної задачі?
Перевірка Android-проєкту після запуску
Після створення Empty Activity дочекайтеся Gradle sync і запустіть незмінений шаблон, щоб відділити проблему середовища від власного коду. Потім замініть MainActivity демонстрацією, зберігаючи package свого проєкту. Якщо назва package відрізняється, виправте перший рядок файла та відповідне розташування у src/main/java або src/main/kotlin.
Перевірте введення порожнього тексту, пробілів, літер, −1,0,59,60,1440 та 1441. Для кожного очікування запишіть до натискання кнопки. Межі 0 і 1440 допустимі за контрактом прикладу. Зміна поля після обчислення повинна мати зрозумілу реакцію: визначте, чи попередній результат залишається, чи очищується, і поясніть це у власній роботі.
Поверніть пристрій або емулятор в іншу орієнтацію. Введений текст має відновитися завдяки rememberSaveable для підтримуваного типу. Окремо перевірте великий системний шрифт і появу клавіатури. Якщо нижня кнопка стає недоступною, перегляньте відступи та можливість прокручування відповідно до вашого макета.
У Logcat спочатку шукайте першу причину власного винятку й стек із файлами проєкту. Compilation error видно в Build Output до запуску; network або runtime-помилка має інший етап. У звіті додайте шлях MainActivity, версії середовища, таблицю введення й скріншоти двох різних станів. Індивідуальний конвертер повинен мати власні одиниці, діапазон та правила округлення.
Схема процесу
Поле зберігає текст як стан.
Самоперевірка
Як обґрунтувати відповідь своїми словами?
Підсумок
Кероване введення відокремлює текст, перевірку та результат. Compose state забезпечує оновлення, а rememberSaveable — визначене відновлення невеликих даних. Власний контракт перевіряють на границях і помилках.