From 936445e335ea3bf350c24841efc3c285494e2983 Mon Sep 17 00:00:00 2001 From: Dmitriy Bulygin Date: Sat, 14 Feb 2026 10:43:06 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=201:=20=D0=9F=D0=B5=D1=80=D0=B5=D0=BF=D0=B8=D1=81=D1=8B=D0=B2?= =?UTF-8?q?=D0=B0=D0=BD=D0=B8=D0=B5=20XML=20=D0=BD=D0=B0=20Compose?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../composehomework/ui/task1/Task1Screen.kt | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/ru/otus/composehomework/ui/task1/Task1Screen.kt b/app/src/main/java/ru/otus/composehomework/ui/task1/Task1Screen.kt index baea737..f8d0062 100644 --- a/app/src/main/java/ru/otus/composehomework/ui/task1/Task1Screen.kt +++ b/app/src/main/java/ru/otus/composehomework/ui/task1/Task1Screen.kt @@ -1,6 +1,15 @@ package ru.otus.composehomework.ui.task1 -import androidx.compose.runtime.Composable +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp /** * Задание 1: Переписывание XML на Compose @@ -22,5 +31,31 @@ import androidx.compose.runtime.Composable */ @Composable fun Task1Screen() { - // TODO: Реализуйте экран на Compose + var welcomeText by remember { mutableStateOf("Добро пожаловать!") } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(32.dp) + ) { + Text( + text = welcomeText, + fontSize = 24.sp, + fontWeight = FontWeight.Bold + ) + + Image( + painter = painterResource(id = android.R.drawable.ic_dialog_info), + contentDescription = "Welcome", + modifier = Modifier.size(120.dp) + ) + + Button( + onClick = { welcomeText = "Кнопка нажата!" } + ) { + Text("Нажми меня") + } + } } From adfbdbf49ca34ab51cfa7b4e4c0865c0c8038675 Mon Sep 17 00:00:00 2001 From: Dmitriy Bulygin Date: Sat, 14 Feb 2026 10:51:20 +0300 Subject: [PATCH 2/4] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=202:=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2=D0=BB=D0=B5=D0=BD=D0=B8?= =?UTF-8?q?=D0=B5=20=D1=81=D0=BE=D1=81=D1=82=D0=BE=D1=8F=D0=BD=D0=B8=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../composehomework/ui/task2/Task2Screen.kt | 42 ++++++++++++++++++- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt b/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt index 9b625d4..d96216f 100644 --- a/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt +++ b/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt @@ -1,6 +1,15 @@ package ru.otus.composehomework.ui.task2 -import androidx.compose.runtime.Composable +import androidx.compose.foundation.Image +import androidx.compose.foundation.layout.* +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp /** * Задание 2: Добавление состояния @@ -20,5 +29,34 @@ import androidx.compose.runtime.Composable */ @Composable fun Task2Screen() { - // TODO: Реализуйте экран с состоянием счетчика + var welcomeText by remember { mutableStateOf("Добро пожаловать!") } + var count by remember { mutableStateOf(0) } + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.spacedBy(32.dp) + ) { + Text( + text = welcomeText, + fontSize = 24.sp, + fontWeight = FontWeight.Bold + ) + + Image( + painter = painterResource(id = android.R.drawable.ic_dialog_info), + contentDescription = "Welcome", + modifier = Modifier.size(120.dp) + ) + + Text(text = "Счетчик: $count") + + Button( + onClick = { count++ } + ) { + Text("Нажми меня") + } + } } From 105af69e0daa7c00ee64fe3cf0c9133f9ca01834 Mon Sep 17 00:00:00 2001 From: Dmitriy Bulygin Date: Sat, 14 Feb 2026 11:38:01 +0300 Subject: [PATCH 3/4] =?UTF-8?q?=D0=97=D0=B0=D0=B4=D0=B0=D0=BD=D0=B8=D0=B5?= =?UTF-8?q?=203:=20ViewModel=20=D0=B8=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BB?= =?UTF-8?q?=D0=B5=D0=BA=D1=81=D0=BD=D0=BE=D0=B5=20=D1=81=D0=BE=D1=81=D1=82?= =?UTF-8?q?=D0=BE=D1=8F=D0=BD=D0=B8=D0=B5=20(MVI=20=D0=BF=D0=B0=D1=82?= =?UTF-8?q?=D1=82=D0=B5=D1=80=D0=BD)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../composehomework/ui/task3/Task3Screen.kt | 187 +++++++++++++++++- app/src/main/res/values/strings.xml | 1 + 2 files changed, 186 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/ru/otus/composehomework/ui/task3/Task3Screen.kt b/app/src/main/java/ru/otus/composehomework/ui/task3/Task3Screen.kt index 8cffe89..cd1ea7b 100644 --- a/app/src/main/java/ru/otus/composehomework/ui/task3/Task3Screen.kt +++ b/app/src/main/java/ru/otus/composehomework/ui/task3/Task3Screen.kt @@ -1,6 +1,17 @@ package ru.otus.composehomework.ui.task3 -import androidx.compose.runtime.Composable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import ru.otus.composehomework.R /** * Задание 3: Подключение ViewModel и комплексное состояние (MVI паттерн) @@ -44,5 +55,177 @@ import androidx.compose.runtime.Composable */ @Composable fun Task3Screen() { - // TODO: Реализуйте экран с ViewModel и формой + val viewModel: Task3ViewModel = viewModel() + val state by viewModel.state.collectAsState() + + Column( + modifier = Modifier + .fillMaxSize() + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + Text( + text = "Форма обратной связи", + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + modifier = Modifier.padding(bottom = 8.dp) + ) + + TextField( + value = state.name, + onValueChange = { + viewModel.handleIntent(Task3Intent.NameChanged(it)) + }, + label = { Text("Имя") }, + placeholder = { Text(stringResource(R.string.name_hint)) }, + modifier = Modifier.fillMaxWidth(), + isError = state.validationErrors?.errors?.get(FieldType.NAME) != null, + supportingText = state.validationErrors?.errors?.get(FieldType.NAME)?.let { + { Text(it) } + }, + enabled = state.uiState !is UiState.Loading + ) + + TextField( + value = state.email, + onValueChange = { + viewModel.handleIntent(Task3Intent.EmailChanged(it)) + }, + label = { Text("Email") }, + placeholder = { Text(stringResource(R.string.email_hint)) }, + modifier = Modifier.fillMaxWidth(), + isError = state.validationErrors?.errors?.get(FieldType.EMAIL) != null, + supportingText = state.validationErrors?.errors?.get(FieldType.EMAIL)?.let { + { Text(it) } + }, + enabled = state.uiState !is UiState.Loading + ) + + TextField( + value = state.message, + onValueChange = { + viewModel.handleIntent(Task3Intent.MessageChanged(it)) + }, + label = { Text("Сообщение") }, + placeholder = { Text(stringResource(R.string.message_hint)) }, + modifier = Modifier.fillMaxWidth(), + minLines = 3, + isError = state.validationErrors?.errors?.get(FieldType.MESSAGE) != null, + supportingText = state.validationErrors?.errors?.get(FieldType.MESSAGE)?.let { + { Text(it) } + }, + enabled = state.uiState !is UiState.Loading + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Button( + onClick = { + viewModel.handleIntent(Task3Intent.SubmitClicked) + }, + enabled = state.uiState !is UiState.Loading, + modifier = Modifier.weight(1f) + ) { + Text(stringResource(R.string.submit)) + } + + OutlinedButton( + onClick = { + viewModel.handleIntent(Task3Intent.ClearClicked) + }, + enabled = state.uiState !is UiState.Loading, + modifier = Modifier.weight(1f) + ) { + Text(stringResource(R.string.clear)) + } + } + + when (state.uiState) { + is UiState.Loading -> { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 16.dp), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + + is UiState.Success -> { + val successState = state.uiState as UiState.Success + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(8.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.primaryContainer.copy(alpha = 0.3f) + ) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "Успешно!", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold + ) + Text( + text = "Форма успешно отправлена!", + style = MaterialTheme.typography.bodyMedium + ) + Text( + text = "ID: ${successState.result.id}", + style = MaterialTheme.typography.bodySmall + ) + } + } + } + + is UiState.Error -> { + val errorState = state.uiState as UiState.Error + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(8.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.errorContainer.copy(alpha = 0.3f) + ) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = "Ошибка", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.error + ) + Text( + text = errorState.message, + style = MaterialTheme.typography.bodyMedium + ) + Button( + onClick = { + viewModel.handleIntent(Task3Intent.RetryClicked) + }, + modifier = Modifier.fillMaxWidth() + ) { + Text(stringResource(R.string.retry)) + } + } + } + } + + is UiState.ValidationError -> { + // Ошибки валидации уже обработаны + } + + is UiState.Idle -> { + // Форма готова к заполнению + } + } + } } diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 26e74fc..c9469be 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -13,4 +13,5 @@ Отправить Повторить Загрузка... + Очистить From 539faa52f4ce28f5a88eed9ee26fd7c763ebe328 Mon Sep 17 00:00:00 2001 From: Dmitriy Bulygin Date: Sat, 14 Feb 2026 11:39:29 +0300 Subject: [PATCH 4/4] =?UTF-8?q?Task2-fix:=20=D0=B8=D0=B7=D0=BC=D0=B5=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=BE=D0=B2=D0=B5=D0=B4=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=8F=20welcomeText?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt b/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt index d96216f..c95c5eb 100644 --- a/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt +++ b/app/src/main/java/ru/otus/composehomework/ui/task2/Task2Screen.kt @@ -29,8 +29,8 @@ import androidx.compose.ui.unit.sp */ @Composable fun Task2Screen() { - var welcomeText by remember { mutableStateOf("Добро пожаловать!") } var count by remember { mutableStateOf(0) } + val welcomeText = "Кнопка нажата $count раз!" Column( modifier = Modifier