Добавить в корзинуПозвонить
Найти в Дзене

Урок 9: TextField, OutlinedTextField, Валидация почты, ErrorState | Курс Android Jetpack Compose

Смотреть на обучающей платформе с ИИ: https://androidsprint.ru/courses/jetpack-compose-osnovy Смотреть в VK:
https://vkvideo.ru/video-85562117_456239145?pl=-85562117_3 Смотреть на YouTube:
https://www.youtube.com/watch?v=yJoD0v00uBQ В прошлом уроке мы разобрались в критически важной теме, связанной с хранением состояния и рекомпозицией. Теперь мы можем работать с большим количеством composable функций, которые не могут работать без объявления стейта. В качестве демонстрационной задачи мы реализуем ввод электропочты в текстовое поле. Сначала быстрая подготовка. Как видите, у меня в уже стандартной для предыдущих уроков обертке вызвана одна единственная функция StudyAppHeader(). Однако, ее тело находится в другом файле. Также внизу у меня уже есть заготовка кнопки, которая нам понадобится в следующем уроке для продолжения реализации логики гипотетической регистрации. Тут практически ничего нового нет. Она по прежнему не совершает никакого действия (пустой onClick), добавлено немного офо
Оглавление
Смотреть на обучающей платформе с ИИ: https://androidsprint.ru/courses/jetpack-compose-osnovy
Смотреть в VK:
https://vkvideo.ru/video-85562117_456239145?pl=-85562117_3
Смотреть на YouTube:
https://www.youtube.com/watch?v=yJoD0v00uBQ

Введение

В прошлом уроке мы разобрались в критически важной теме, связанной с хранением состояния и рекомпозицией. Теперь мы можем работать с большим количеством composable функций, которые не могут работать без объявления стейта. В качестве демонстрационной задачи мы реализуем ввод электропочты в текстовое поле.

Сначала быстрая подготовка. Как видите, у меня в уже стандартной для предыдущих уроков обертке вызвана одна единственная функция StudyAppHeader().

-2

Однако, ее тело находится в другом файле.

  • Во-первых так можно делать, все хорошо.
  • Во-вторых я добавлю параметры заголовка и подзаголовка в функцию, чтобы переиспользовать ее с любыми другими строками. Сохраняя при этом единый стиль. Тоже хорошая практика, чтобы функции были универсальными и переиспользуемыми. А также благодаря передаче данных через. параметры можно выносить бизнес-логику в отдельные функции или еще лучше во ViewModel.
-3

Также внизу у меня уже есть заготовка кнопки, которая нам понадобится в следующем уроке для продолжения реализации логики гипотетической регистрации. Тут практически ничего нового нет. Она по прежнему не совершает никакого действия (пустой onClick), добавлено немного оформления в виде скругления углов, высоты, отступов по горизонтали и стиля текста. Все.

-4

Приступим к созданию текстового поля для ввода электронной почты. Пусть оно и текст для результатов гипотетической регистрации будут располагаться в отдельной функции.

В XML верстке мы использовали компонент EditText. В Jetpack Compose такого компонента нет. Здесь используется TextField — аналогичный компонент, который выполняет ту же функцию, что и EditText. Он позволяет вводить и редактировать текст.

Если ввести TextField, то можем увидеть и другие компоненты, помеченные значком Compose.

  • OutlinedTextField имеет тот же функционал, что и TextField, но поле отображается с обводкой (или с рамкой).
  • BasicTextField – более низкоуровневый компонент для текстового ввода, который дает больше контроля над стилизацией и поведением. Используется в основном для создания собственных текстовых полей.
-5

Создание текстового поля OutlinedTextField

Выбираем OutlinedTextField, он просто больше нам подходит по стилю. Вы можете попробовать все варианты. Функция имеет множество специализированных параметров для настройки внешнего вида и поведения. Первые два обязательные.

  • value – это поле, которое должно отображать содержимое этого самого текстового поля. Не путать с плейсхолдером. Мы можем вписать сюда какой-нибудь текст и он будет отображаться в поле так, как будто его уже вписали заранее. Для примера что-то напишу сюда.
  • onValueChange – работает точно также, как и onCheckedChange на предыдущем уроке. Только там при клике в лямбду передавалось значение. Здесь же при каждом изменении текстового поля эта лямбда будет обновляться. Неважно вводим мы какой-то символ или удаляем.
  • shape – скопирую кнопки, это добавит единого стиля.
  • textStyle – добавлю по той же причине из переопределенных нами ранее стилей.

Вызовем функцию в setContent и посмотрим что получилось. Обратите внимание, из поля value строка отобразилась в текстовом поле. Курсор активируется, но ничего напечатать или стереть невозможно.

-6

Причина все та же, что и с чекбоксом. Compose не вызывает рекомпозицию, потому, что не знает в какой момент это нужно делать, да и не написан требуемый инструментарий. Воспользуемся аналогичным объектом MutableState в связке с remember. Импортируем делегат by и значение по умолчанию перенесу из value в стейт. И не забываем в лямбде при изменении текстового поля присваивать значения нашему стейту. Теперь можно проверять.

-7

Добавление Placeholder

Отлично. Стейт подхватывает изменения при вводе каждого символа и вызывает рекомпозицию. Теперь уберу значение по умолчанию и добавлю плейсхолдер с помощью специализированного для OutlinedTextField параметра функции. Placeholder последним параметром принимает composable лямбду, поэтому строка там устанавливается обычной функцией Text с оформлением по нашим предпочтениям. Добавим стиля и цвета.

Добавление SingleLine

Работает хорошо, но если я буду нажимать enter, то по умолчанию текстовое поле будет увеличиваться.

-8

Нам такое поведение не нужно, поэтому добавлю параметр singleLine = true. По умолчанию он установлен в положение false.

Добавление Label

Есть еще один крутой атрибут для стилизации – label. Точно также задаем какой-то текст внутри. На стиль не обращайте внимания, я его переопределил по собственным размерам. В итоге мы получаем вот такую анимацию лейбла при активации текстового поля. Ну, это красиво.

-9

Добавление иконки с кнопкой в текстовое поле – IconButton

Следующая пара параметров отвечает за иконки внутри текстового поля. Из них можно сделать кнопки с каким-нибудь функционалом.

  • leadingIcon добавит иконку в начало поля,
  • trailingIcon добавляет в конец.

Этот параметр принимает composable функцию, следовательно в нее отправляем то, что хотим отобразить. Пусть это будет IconButton – смотрим в декларацию, у этой функции первый параметр onClick обязательный. То есть что будет происходить при нажатии – оставим пока пустую лямбду.

Ну а чтобы добавить само изображение, нужно последним параметром IconButton – то есть в лямбде вызвать функцию Icon. Здесь принцип схож с добавлением картинки. Первым параметром задается сама иконка, взять ее можно из стандартного набора предоставляемого Material: imageVector = Icons.Filled.Clear. Второй параметр contentDescription тоже обязательный – добавим описание иконки. Готово.

Смотрим. Иконка отрисовалась, она кликабельна, все хорошо.

-10

И мы можем задать ей какое-то полезное действие очень просто. Пусть она очищает введеный текст.

Как это сделать? Во-первых обратить внимание на пустую лямбду – здесь должно что-то происходить по клику на иконку. Во-вторых обратить внимание на изменяемую переменную со стейтом, которая при изменении вызывает рекомпозицию. Следовательно здесь достаточно просто задавать стейту пустую строку. И она сразу же отрисуется в TextField. Проверяем – и все происходит именно так. Великолепно.

-11

Валидация email и ErrorState

Раз уж мы оформляем поле для ввода электронной почты, стоит добавить валидацию. Проверять, что пользователь ввел действительно почту по шаблону. Для этого есть стандартные механизмы.

Для начала воспользуемся у нашей composable функции параметром isError. Если его установить в true и посмотреть что будет – обнаружим текстовое поле в состоянии “Ошибка”. Оно окрашено в красный.

-12

Как правило в таком состоянии нельзя отправить форму, если данные отсутствуют или не верны. Мы просто внедрим логику валидации на корректность имейла.

Как правило в Compose все логично, то есть как слышится, так и пишется. Когда я говорил про состояние “Ошибка” – я буквально имел в виду, что нам нужно создать дополнительный стейт для хранения состояния ошибки. Сделаем это.

-13

Мы будем хранить строку, хотя в стейте можно хранить что угодно. Например, Boolean – флаг есть ошибка или нет, код конкретной полученной ошибки или целый объект, предварительно создав для него класс. То же самое относится и к обычным стейтам, по сути они ничем не отличаются, мы просто решили, что этот объект MutableState будет отвечать за хранение ошибок в данной функции.

Теперь самое интересное. Где валидировать введенный текст? Конечно, в onValueChange, после каждого введенного символа. Так что textState мы не трогаем, чтобы текст отрисовывался как положено. А далее присваиваем errorState следующий код.

Код проверяет, соответствует ли введённый текст формату электронной почты с помощью регулярного выражения. Если текст правильный, то errorState устанавливается в пустую строку (что будет означать отсутствие ошибок), а если неправильный — присваивается сообщение «Введите корректный email», чтобы уведомить пользователя о необходимости исправить ввод.

Наконец, сейчас у нас isError захардкожен. Добавим запись о том, что показывать ошибку только если стейт с ошибкой не пустой (то есть не является пустой строкой) – isError = errorState.isNotEmpty(),.

Запускаем. Вот что получается. При вводе каждого символа происходит валидация строки и пока она не похожа на почту – в стейт записывается “Введите корректный email”.

-14

Как только я добавляю в конце .ru – ошибка пропадает.

Давайте выведем в лейбл текст ошибки – пусть она отображается пока ошибка активна, а когда ошибки нет – показывается стандартный лейбл.

-15

Последний штрих – очищать errorState при клике на кнопку очистки поля, иначе сейчас очищается только стейт с текстом, а ошибка остается.

-16

Добавлю errorState = "".

Выглядит довольно неплохо.

-17

В следующем уроке мы продолжим развивать функционал экрана с регистрацией и конкретно рассмотрим как работают стейты не только в рамках одной функции, а для всего экрана.

Дополнительные материалы

🔹 Обучающая платформа с ИИ: https://androidsprint.ru

🔹 Интерактивный курс Kotlin Практикум: https://androidsprint.ru/kotlin-praktikum

🔹 Код из всех уроков в телеграм канале: https://t.me/ievetrov

🔹 Сообщество в VK: https://vk.com/ivan.ievetrov