Text input layout error

Создание выпадающего меню

Создание выпадающего меню

Макет TextInputLayout сначала появился в библиотеке Android Design Support Library и добавляет немного красоты к текстовому полю. Когда пользователь начинает вводить текст в текстовом поле, то подсказка, заданная в этом компоненте, всплывает над ним в специальном TextView. Пример можно увидеть на видео.

Библиотека больше не развивается, поэтому используйте AndroidX, которую я и буду теперь использовать в описании.

Компонент можно найти на панели инструментов в разделе Text.

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


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".Main2Activity">

    <com.google.android.material.textfield.TextInputLayout
        android:id="@+id/textInputLayout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <com.google.android.material.textfield.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="hint" />
    </com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

Так выглядел компонент в составе Support Library.

Подсказку не обязательно указывать в атрибуте android:hint у текстового поля. Можно программно присвоить через метод:


textInputLayout.setHint(getString(R.string.hint));

Стилизация

В AndroidX у компонента появились новые стили: OutlinedBox, FilledBox и другие. Можете самостоятельно попробовать пример с двумя стилями и посмотреть на отличия.


<com.google.android.material.textfield.TextInputLayout
    style="@style/Widget.MaterialComponents.TextInputEditText.OutlinedBox"
    ... />

<com.google.android.material.textfield.TextInputLayout
    style="@style/Widget.MaterialComponents.TextInputLayout.FilledBox"
    ... />

Общая стилизация доступна следующим образом. Пропишем стили в styles.xml:


<!--Floating label text style-->  
<style name="MyHintText" parent="TextAppearance.AppCompat.Small">  
    <item name="android:textColor">@color/pink</item>
</style>

<!--Input field style-->  
<style name="MyEditText" parent="Theme.AppCompat.Light">  
    <item name="colorControlNormal">@color/indigo</item>
    <item name="colorControlActivated">@color/pink</item>
</style>  

Применим стили


<com.google.android.material.textfield.TextInputLayout  
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:hintTextAppearance="@style/MyHintText">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/Title"
        android:theme="@style/MyEditText" />

</com.google.android.material.textfield.TextInputLayout>

Обработка ошибки

Предыдущий пример показывал применение подсказки. Но также можно выводить сообщения об ошибке. Здесь потребуется написать немного кода. За вывод ошибки отвечает атрибут app:errorEnabled. Назначим текстовому полю тип клавиатуры и однострочный текст. При наборе текста после нажатия на клавишу Enter проверяем длину текста. Если текст меньше четырёх символов, то выводим сообщение об ошибке.


Добавить пространство имён к корневому элементу
xmlns:app="http://schemas.android.com/apk/res-auto"

<com.google.android.material.textfield.TextInputLayout
    android:id="@+id/textInputLayout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:errorEnabled="true">

    <EditText
        android:id="@+id/editTextName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:imeOptions="actionGo"
        android:inputType="text"
        android:singleLine="true"/>
</com.google.android.material.textfield.TextInputLayout>

Код


package ru.alexanderklimov.design;

import ...

public class MainActivity extends AppCompatActivity {

    private static final int MIN_TEXT_LENGTH = 4;
    private static final String EMPTY_STRING = "";

    private TextInputLayout mTextInputLayout;
    private EditText mEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_main);

        mTextInputLayout = findViewById(R.id.textInputLayout);
        mEditText = findViewById(R.id.editTextName);

        mTextInputLayout.setHint(getString(R.string.hint));
        mEditText.setOnEditorActionListener(ActionListener.newInstance(this));
    }

    private boolean shouldShowError() {
        int textLength = mEditText.getText().length();
        return textLength > 0 && textLength < MIN_TEXT_LENGTH;
    }

    private void showError() {
        mTextInputLayout.setError(getString(R.string.error));
    }

    private void hideError() {
        mTextInputLayout.setError(EMPTY_STRING);
    }

    private static final class ActionListener implements TextView.OnEditorActionListener {
        private final WeakReference<MainActivity> mainActivityWeakReference;

        public static ActionListener newInstance(MainActivity mainActivity) {
            WeakReference<MainActivity> mainActivityWeakReference = new WeakReference<>(mainActivity);
            return new ActionListener(mainActivityWeakReference);
        }

        private ActionListener(WeakReference<MainActivity> mainActivityWeakReference) {
            this.mainActivityWeakReference = mainActivityWeakReference;
        }

        @Override
        public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
            MainActivity mainActivity = mainActivityWeakReference.get();
            if (mainActivity != null) {
                if (actionId == EditorInfo.IME_ACTION_GO && mainActivity.shouldShowError()) {
                    mainActivity.showError();
                } else {
                    mainActivity.hideError();
                }
            }
            return true;
        }
    }
}

Текст ошибки выводится снизу от текстового поля.

TextInputLayout

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


app:errorEnabled="true"
app:errorTextAppearance="@style/ErrorText"

В файле стилей res/values/styles.xml добавим новый стиль:


<style name="ErrorText" parent="TextAppearance.AppCompat.Small">
    <item name="android:textStyle">bold|italic</item>
    <item name="android:textColor">@color/colorPrimary</item>
</style>

Теперь выводится другим цветом.

TextInputLayout

Расширенный вид стилей:


<!--Error label text style-->  
<style name="MyErrorText" parent="TextAppearance.AppCompat.Small">  
    <item name="android:textColor">@color/colorPrimary</item>
</style>

<!--Input field style-->  
<style name="MyEditText" parent="Theme.AppCompat.Light">  
    <item name="colorControlNormal">@color/indigo</item>
    <item name="colorControlActivated">@color/pink</item>
</style>  

Применяем через атрибуты app:errorTextAppearance и android:theme.


<com.google.android.material.textfield.TextInputLayout  
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:errorTextAppearance="@style/MyErrorText"
    app:errorEnabled="true">

    <EditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="@string/Title"
        android:theme="@style/MyEditText" />

</com.google.android.material.textfield.TextInputLayout>

Счётчик символов

С помощью атрибутов app:counterEnabled и app:counterMaxLength можно установить счётчик символов с указанием предела, который будет выводиться под текстовым полем.


<com.google.android.material.textfield.TextInputLayout
    ...
    app:counterEnabled="true"
    app:counterMaxLength="140">

    <EditText
        .../>

</com.google.android.material.textfield.TextInputLayout>

Counter TextInputLayout

Когда будет превышен лимит, то цвет счётчика изменится. Этот цвет можно стилизовать через стиль.


<style name="MyCounterText" parent="TextAppearance.AppCompat.Small">
    <item name="android:textColor">@android:color/holo_orange_dark</item>
</style>

Стиль применяется к атрибуту app:counterOverflowTextAppearance:


<com.google.android.material.textfield.TextInputLayout
    ...
    app:counterOverflowTextAppearance="@style/MyCounterText"
    app:counterEnabled="true"
    app:counterMaxLength="10">

Связка TextInputLayout и AutoCompleteTextView позволяет создать выпадающее меню взамен стандартного компонента Spinner. Для этого задействуем один стилей, в котором присутствует ExposedDropdownMenu.

TextInputLayout ExposedDropdownMenu

Разметка экрана.


<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.textfield.TextInputLayout
        style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_marginStart="16dp"
        android:layout_marginEnd="16dp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

        <AutoCompleteTextView
            android:id="@+id/autoCompleteTextView"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:inputType="none"
            android:text="Барсик" />
    </com.google.android.material.textfield.TextInputLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

В AutoCompleteTextView установлен тип android:inputType=»none», чтобы у пользователя не было возможности изменять текст. Ведь нам нужно меню, а не текстовое поле.

Для создания элементов меню воспользуемся массивом строк в ресурсах. Добавляем в res/values/strings.xml.


<string-array name="cats">
    <item>Барсик</item>
    <item>Мурзик</item>
    <item>Рыжик</item>
    <item>Васька</item>
</string-array>

Создадим отдельную разметку для элементов меню в файле res/layout/dropdown_item.xml.


<?xml version="1.0" encoding="utf-8"?>
<TextView 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="14dp"
    android:textStyle="bold">
</TextView>

Присоединяем созданный макет к AutoCompleteTextView через адаптер.


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val autoCompleteTextView: AutoCompleteTextView = findViewById(R.id.autoCompleteTextView)

    val cats = resources.getStringArray((R.array.cats))
    val arrayAdapter = ArrayAdapter(this, R.layout.dropdown_item, cats)

    autoCompleteTextView.setAdapter(arrayAdapter)
}

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

TextInputLayout ExposedDropdownMenu

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


class MainActivity : AppCompatActivity() {

    private lateinit var autoCompleteTextView: AutoCompleteTextView

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        autoCompleteTextView = findViewById(R.id.autoCompleteTextView)
    }

    override fun onResume() {
        super.onResume()

        val cats = resources.getStringArray((R.array.cats))
        val arrayAdapter = ArrayAdapter(this, R.layout.dropdown_item, cats)

        autoCompleteTextView.setAdapter(arrayAdapter)
    }
}

Можно добавить к элементам меню значок. Заодно добавим подсказку.


<com.google.android.material.textfield.TextInputLayout
    style="@style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
    android:layout_width="0dp"
    android:layout_height="wrap_content"
    android:layout_marginStart="16dp"
    android:layout_marginEnd="16dp"
    android:hint="Коты"
    app:startIconDrawable="@drawable/ic_pets"
    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent">

TextInputLayout ExposedDropdownMenu

TextInputEditText

Казалось бы простой компонент, никаких трудностей не возникает. Но не торопитесь. Стоит повернуть устройство в альбомный режим, как текстовое поле растянется на весь экран и никакой подсказки вы не увидите. Возможно, это баг, который когда-нибудь починят. Но проблему легко решить, если вместо стандартного EditText использовать специальный компонент TextInputEditText:


<com.google.android.material.textfield.TextInputLayout
    ...>

    <com.google.android.material.textfield.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Введите текст"/>

</com.google.android.material.textfield.TextInputLayout>

Дополнительное чтение

TextInputEditText

Реклама

Содержание

  1. TextInputLayout
  2. Стилизация
  3. Обработка ошибки
  4. Счётчик символов
  5. Создание выпадающего меню
  6. TextInputEditText
  7. TextInputLayout Styling
  8. Hint color
  9. Label, Helper and Error
  10. Fonts
  11. Spaces
  12. Bottom line color
  13. Box background-color
  14. Cursor and Selection
  15. TextInputLayout Styling
  16. Hint color
  17. Label, Helper and Error
  18. Fonts
  19. Spaces
  20. Bottom line color
  21. Box background-color
  22. Cursor and Selection
  23. Валидация элементов формы textInputLayout в Android с помощью связывания данных
  24. Удобный способ валидации форм
  25. Что нам потребуется?
  26. https://github.com/Mustufa786/TextInputLayout-FormValidation

TextInputLayout

Макет TextInputLayout сначала появился в библиотеке Android Design Support Library и добавляет немного красоты к текстовому полю. Когда пользователь начинает вводить текст в текстовом поле, то подсказка, заданная в этом компоненте, всплывает над ним в специальном TextView. Пример можно увидеть на видео.

Библиотека больше не развивается, поэтому используйте AndroidX, которую я и буду теперь использовать в описании.

Компонент можно найти на панели инструментов в разделе Text.

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

Так выглядел компонент в составе Support Library.

Подсказку не обязательно указывать в атрибуте android:hint у текстового поля. Можно программно присвоить через метод:

Стилизация

В AndroidX у компонента появились новые стили: OutlinedBox, FilledBox и другие. Можете самостоятельно попробовать пример с двумя стилями и посмотреть на отличия.

Общая стилизация доступна следующим образом. Пропишем стили в styles.xml:

Обработка ошибки

Предыдущий пример показывал применение подсказки. Но также можно выводить сообщения об ошибке. Здесь потребуется написать немного кода. За вывод ошибки отвечает атрибут app:errorEnabled. Назначим текстовому полю тип клавиатуры и однострочный текст. При наборе текста после нажатия на клавишу Enter проверяем длину текста. Если текст меньше четырёх символов, то выводим сообщение об ошибке.

Текст ошибки выводится снизу от текстового поля.

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

В файле стилей res/values/styles.xml добавим новый стиль:

Теперь выводится другим цветом.

Расширенный вид стилей:

Применяем через атрибуты app:errorTextAppearance и android:theme.

Счётчик символов

С помощью атрибутов app:counterEnabled и app:counterMaxLength можно установить счётчик символов с указанием предела, который будет выводиться под текстовым полем.

Когда будет превышен лимит, то цвет счётчика изменится. Этот цвет можно стилизовать через стиль.

Стиль применяется к атрибуту app:counterOverflowTextAppearance:

Создание выпадающего меню

Связка TextInputLayout и AutoCompleteTextView позволяет создать выпадающее меню взамен стандартного компонента Spinner. Для этого задействуем один стилей, в котором присутствует ExposedDropdownMenu.

В AutoCompleteTextView установлен тип android:inputType=»none», чтобы у пользователя не было возможности изменять текст. Ведь нам нужно меню, а не текстовое поле.

Для создания элементов меню воспользуемся массивом строк в ресурсах. Добавляем в res/values/strings.xml.

Создадим отдельную разметку для элементов меню в файле res/layout/dropdown_item.xml.

Присоединяем созданный макет к AutoCompleteTextView через адаптер.

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

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

Можно добавить к элементам меню значок. Заодно добавим подсказку.

TextInputEditText

Казалось бы простой компонент, никаких трудностей не возникает. Но не торопитесь. Стоит повернуть устройство в альбомный режим, как текстовое поле растянется на весь экран и никакой подсказки вы не увидите. Возможно, это баг, который когда-нибудь починят. Но проблему легко решить, если вместо стандартного EditText использовать специальный компонент TextInputEditText:

Источник

TextInputLayout Styling

Today, with the material components, we have at least 3 out of box implementations of input layout: Default, FilledBox, and OutlinedBox. And we want to briefly walk through their styling:

If you are looking for a brief solution you can use this table. Below you can find the description of each of these parameters in detail.

Hint color

Hint color could be set via “android:textColorHint” parameter of TextInputLayout. This parameter also changes the label default color (label focused color could also be changed in other ways). Let’s set a purple color (#673AB7) as an example.

Label, Helper and Error

Such parameters as “app:hintTextAppearance”, “app:helperTextTextAppearance” and “app:errorTextAppearance” together with all the necessary text parameters of styles.xml should be used to customize labels, helpers and errors in TextInputLayout. The parent of the text appearance style should be TextAppearance.AppCompat or some of its children.

Also, please keep in mind the following:

  • “ app:hintTextAppearance” affects the focused label color and label size in any state;
  • when an error is shown, the bottom/border line will have the color indicated in the “android:textColor” parameter of errorTextAppearance. This color will be changed to the default once the error is removed.

Here is the TextAppearances for error and helper that was used in the above shown TextInputLayouts:

Fonts

Fonts of all elements except inputted text (label, hint, error, and helper) could be easily changed in the program via the typeface parameter of TextInputLayout. We have done it in the following way:

Spaces

Label’s, Helper’s and Error’s spaces are connected to the EditText in the TextInputLayout. So, to increase or decrease spaces between the error/helper messages and bottom line/border you should use “android:layout_marginBottom” parameter, between the label and the top of the text, or add some space on the start of the error, helper and the label, and you should set positive or negative padding to the EditText. But you should understand that this will affect the text inside the InputLayout so, it would be better if horizontal spaces were symmetric from both sides.

As an example, let’s increase space above the errors for Default and OutlinedBox input layouts and decrease for FilledBox input layout. Also, let’s add some extra space at the start of the input layouts.

Bottom line color

Bottom line color could be changed with “app:backgroundTint” attribute of EditText view. Pay attention to the prefix, “app:” that makes this parameter back-compatible and useful even for Android API 16.

As for the OutlinedBox, it does not have the bottom line but has an outline instead. To change its color we should use “app:boxStrokeColor” parameter, but this parameter changes stroke color in the focused state only. Changing the default state of the stroke is a bit tricky. We should override mtrl_textinput_default_box_stroke_color color. The line below should be added to the color.xml file:

Let’s make the bottom line and the outline stroke color purple (#673AB7) as well.

Box background-color

This element is present in Filled and Outlined input layouts and can be changed via “app:boxBackgroundColor” parameter. Let’s change this parameter to the transparent purple (#26673AB7) only for FilledBox input layout.

Cursor and Selection

Finally, we get to the most interesting part — how to change the cursor and the selection handles. Most of you have already tried to use “app:textSelectHandle” parameters, that allow changing the drawable of the cursor handle and selection left and right handles. But how to change the color without drawing custom drawables and without changing the main application colors? It is not the secret that the cursor and handles color, as well as label color in focus mode, take their color from the AppTheme “colorAccent”. Of course, we can change it for the whole project but it is not obligatory. We can just use ThemeOverlay and change the “colorAccent” for a single view. We should inherit our style from ThemeOverlay.AppCompat and set it as the “android:theme” parameter of the view and that is all. As for the selection highlight, you can change it via android:textColorHighlight of the EditText.

In the example above was used android:color/holo_blue_light:

So, my final layout looked like this:

colors.xml includes the following colors:

styles.xml includes the following styles:

Tap the 👏 button if you found this article useful!

About the Author
Dmytro is Android Developer at OmiSoft, whose inner perfectionist does not allow to be content with mediocre results but forces him to move forward to excellence.

Need an Android mobile app with clean & maintainable code? Click here to get an estimate! Or find us on Facebook and Twitter.

Источник

TextInputLayout Styling

Today, with the material components, we have at least 3 out of box implementations of input layout: Default, FilledBox, and OutlinedBox. And we want to briefly walk through their styling:

If you are looking for a brief solution you can use this table. Below you can find the description of each of these parameters in detail.

Hint color

Hint color could be set via “android:textColorHint” parameter of TextInputLayout. This parameter also changes the label default color (label focused color could also be changed in other ways). Let’s set a purple color (#673AB7) as an example.

Label, Helper and Error

Such parameters as “app:hintTextAppearance”, “app:helperTextTextAppearance” and “app:errorTextAppearance” together with all the necessary text parameters of styles.xml should be used to customize labels, helpers and errors in TextInputLayout. The parent of the text appearance style should be TextAppearance.AppCompat or some of its children.

Also, please keep in mind the following:

  • “ app:hintTextAppearance” affects the focused label color and label size in any state;
  • when an error is shown, the bottom/border line will have the color indicated in the “android:textColor” parameter of errorTextAppearance. This color will be changed to the default once the error is removed.

Here is the TextAppearances for error and helper that was used in the above shown TextInputLayouts:

Fonts

Fonts of all elements except inputted text (label, hint, error, and helper) could be easily changed in the program via the typeface parameter of TextInputLayout. We have done it in the following way:

Spaces

Label’s, Helper’s and Error’s spaces are connected to the EditText in the TextInputLayout. So, to increase or decrease spaces between the error/helper messages and bottom line/border you should use “android:layout_marginBottom” parameter, between the label and the top of the text, or add some space on the start of the error, helper and the label, and you should set positive or negative padding to the EditText. But you should understand that this will affect the text inside the InputLayout so, it would be better if horizontal spaces were symmetric from both sides.

As an example, let’s increase space above the errors for Default and OutlinedBox input layouts and decrease for FilledBox input layout. Also, let’s add some extra space at the start of the input layouts.

Bottom line color

Bottom line color could be changed with “app:backgroundTint” attribute of EditText view. Pay attention to the prefix, “app:” that makes this parameter back-compatible and useful even for Android API 16.

As for the OutlinedBox, it does not have the bottom line but has an outline instead. To change its color we should use “app:boxStrokeColor” parameter, but this parameter changes stroke color in the focused state only. Changing the default state of the stroke is a bit tricky. We should override mtrl_textinput_default_box_stroke_color color. The line below should be added to the color.xml file:

Let’s make the bottom line and the outline stroke color purple (#673AB7) as well.

Box background-color

This element is present in Filled and Outlined input layouts and can be changed via “app:boxBackgroundColor” parameter. Let’s change this parameter to the transparent purple (#26673AB7) only for FilledBox input layout.

Cursor and Selection

Finally, we get to the most interesting part — how to change the cursor and the selection handles. Most of you have already tried to use “app:textSelectHandle” parameters, that allow changing the drawable of the cursor handle and selection left and right handles. But how to change the color without drawing custom drawables and without changing the main application colors? It is not the secret that the cursor and handles color, as well as label color in focus mode, take their color from the AppTheme “colorAccent”. Of course, we can change it for the whole project but it is not obligatory. We can just use ThemeOverlay and change the “colorAccent” for a single view. We should inherit our style from ThemeOverlay.AppCompat and set it as the “android:theme” parameter of the view and that is all. As for the selection highlight, you can change it via android:textColorHighlight of the EditText.

In the example above was used android:color/holo_blue_light:

So, my final layout looked like this:

colors.xml includes the following colors:

styles.xml includes the following styles:

Tap the 👏 button if you found this article useful!

About the Author
Dmytro is Android Developer at OmiSoft, whose inner perfectionist does not allow to be content with mediocre results but forces him to move forward to excellence.

Need an Android mobile app with clean & maintainable code? Click here to get an estimate! Or find us on Facebook and Twitter.

Источник

Валидация элементов формы textInputLayout в Android с помощью связывания данных

Удобный способ валидации форм

«Чтобы научиться чему-то хорошо, нужно научиться делать это несколькими способами».

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

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

Финальный вид приложения

Уверен, что многие разработчики хотели бы реализовать такой же функционал и удобное взаимодействие с формами. Итак, давайте начнем.

Что нам потребуется?

Я разобью проект на этапы, чтобы легче было понять, что мы делаем.

1. Настроим исходный проект и включим связывание данных в файле build.gradle(:app) , добавив под тег android<> следующую строку:

Для использования элементов textInputLayout и textInputEditText необходимо включить поддержку Material для Android, добавив в файл build.gradle(:app) следующую зависимость:

Создадим макет нашей формы. Я сделаю простой макет, потому что моя цель — определить его основной функционал, а не создать хороший дизайн.

Я создал вот такой простой макет:

Вот содержимое файла activity_main.xml :

Если вас смущают теги , не переживайте — о них я написал в своей предыдущей статье.

Наш макет готов. Теперь займемся кодом.

2. На GIF-анимации, показывающей поведение финального варианта приложения (см. выше), видно, как появляются и исчезают сообщения об ошибках, когда заданные условия принимают значение true. Это происходит потому, что я связал каждое текстовое поле с объектом TextWatcher, к которому постоянно происходит обращение по мере ввода текста пользователем.

В файле MainActivity.kt я создал класс, который унаследован от класса TextWatcher :

Параметр view , который передается в конструктор класса, я опишу позже.

3. Это основная часть. У каждого текстового поля имеется ряд условий, которые должны иметь значение true перед отправкой данных формы. Код, задающий условия для каждого текстового поля, представлен ниже:

4. Теперь необходимо связать каждое текстовое поле с классом textWatcher , который был создан ранее:

Но как класс TextFieldValidation узнает, с каким текстовым полем нужно связываться? Прокрутив статью выше, вы увидите, что я добавил следующий комментарий в один из методов класса TextFieldValidation :

Обратите внимание, что я передаю параметр view в конструктор класса TextFieldValidation , который отвечает за разделение каждого текстового поля и применение каждого из указанных выше методов следующим образом:

Финальный вариант файла MainActivity.kt выглядит так:

Запустим приложение и полюбуемся поведением формы ввода:

Полный исходный код этого проекта можно скачать по ссылке ниже:

https://github.com/Mustufa786/TextInputLayout-FormValidation

Надеюсь, вы узнали из этой статьи что-то новое для себя. Следите за появлением новых статей! Успехов в разработке!

Источник

TextInputLayout is the currently accepted, first party text input widget intended to match material design specs. However the widget itself does not lend itself to be easily styled to fit your needs, and documentation is sparse.

This tutorial comes from my own personal solution to theming and styling a TextInputLayout. It goes beyond just the regular theme colors but also ensuring all components of the layout (hint, label, input, error) have text appearances that you have control over.

Basic TextInputLayout

Your basic TextInputLayout starts out like this:

<android.support.design.widget.TextInputLayout
    android:id="@+id/text_input_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Field 1">
    
  <android.support.design.widget.TextInputEditText
      android:layout_width="match_parent"
      android:layout_height="wrap_content"/>
      
</android.support.design.widget.TextInputLayout>

And basic theme colors are set like this:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
  <!-- Customize your theme here. -->
  <item name="colorPrimary">#3F51B5</item> // (Indigo 500)
  <item name="colorPrimaryDark">#303F9F</item> // (Indigo 700)
  <item name="colorAccent">#00897B</item> // (Teal 600)
</style>

And we get this result:

basic TextInputLayout style

Noteworthy observations:

  • Only colorAccent is used from the basic app theme colors.
  • We’ve applied a custom font via Calligraphy (Fira Sans) for effect. We see here that the hint, label, and input text are properly formatted, but error text is Roboto.

Even more annoying is when an error layout is selected, the label highlight still uses colorAccent which results in this awkward looking UI — a mix of colorAccent and red error, which may or may not clash:

what is this

Colors

Start with defining colors you want to apply:

<color name="error_color">#C62828</color> // Error color (Red 800)
<color name="hint_color_active">#00897B</color> // Active label color (Teal 600)
<color name="hint_color_inactive">#9E9E9E</color> // Inactive label / Hint (Grey 500)

And set up styles and themes like so:

<style name="MyStyle.InputLayout" parent="Widget.Design.TextInputLayout">
  <item name="fontPath">@string/app_font</item>
  <item name="errorTextAppearance">@style/ErrorTextAppearance</item>
  <item name="hintTextAppearance">@style/HintTextAppearance</item>
</style>

<style name="MyTheme.EditText" parent="Theme.AppCompat.Light">
  <!-- Inactive underline color-->
  <item name="colorControlNormal">@color/hint_color_inactive</item>
  
  <!-- Cursor and Active underline color, uses colorAccent by default if not defined-->
  <item name="colorControlActivated">@color/hint_color_active</item>
</style>

<style name="HintTextAppearance" parent="TextAppearance.Design.Hint">
  <!-- Inactive and Active label color, pointing to a selector-->
  <item name="android:textColor">@color/hint_color</item>
</style>

<style name="ErrorTextAppearance" parent="TextAppearance.Design.Error">
  <!-- Error text color-->
  <item name="android:textColor">@color/error_color</item>
</style>

// res/color/hint_color.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
  <item android:state_focused="true" android:color="@color/hint_color_active"/>
  <item android:color="@color/hint_color_inactive"/>
</selector>

And applying them to your views:

<android.support.design.widget.TextInputLayout
    android:id="@+id/text_input_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="Field 1"
    style="@style/MyStyle.InputLayout">
    
  <android.support.design.widget.TextInputEditText
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:theme="@style/MyTheme.EditText"/>
      
</android.support.design.widget.TextInputLayout>

What we can’t accomplish with pure styling alone is:

  • Error label color
  • Error view typeface
  • Error cursor color

new-style-error-select

The Error View

The way TextInputLayout manages its error view thwarts how Calligraphy works. Essentially, Calligraphy only works when inflating views that have a fontPath attribute applied — it can’t apply a font path to a programmatically created view, even if the style with a fontPath is applied programmatically. Because the error view is created programmatically (and not inflated), any fontPath provided in errorTextAppearance is simply ignored.

Another annoying caveat is the exposed setTypeface method, which is intended to allow specifying the error and hint typefaces:

// TextInputLayout
/**
 * Set the typeface to use for the hint and any label views (such as counter and error views).
 *
 * @param typeface typeface to use, or {@code null} to use the default.
 */
public void setTypeface(@Nullable Typeface typeface);

but will fail to apply the typeface to the error view if it’s invoked before setErrorEnabled(true):

public void setTypeface(@Nullable Typeface typeface) {
  ...
  
  // this is null by default until the first time setErrorEnabled(true) is invoked
  if (mErrorView != null) { 
    mErrorView.setTypeface(typeface);
  }
}

So my recommendation is to enable setErrorEnabled in TextInputLayout styles by default:

<style name="MyStyle.InputLayout" parent="Widget.Design.TextInputLayout">
  <item name="errorEnabled">true</item>
</style>

Error View: Typeface

Android 8.0 (API 26) introduced Fonts in XML and you’re encouraged to explore that solution to see if that works for you. I’ll walk through the more lengthy, Calligraphy based solution below.

Create a custom TextInputLayout, and load and apply setTypeface in the constructor using whatever fontPath is applied to the entire layout:

private static final int[] FONT_PATH = new int[] { R.attr.fontPath };

public MyTextInputLayout(...) {
    ...
    Typeface typeface = TypefaceUtils.load(context.getAssets(),
        loadFontPathFromStyle(context, attrs, FONT_PATH));

    // Only works if errorEnabled == true. Either call it here or set it in your style.
    setTypeface(typeface);
  }

  // Implementation from CalligraphyUtils#pullFontPathFromStyle
  private String loadFontPathFromStyle(Context context, AttributeSet attrs, int[] attributeId) {
    if (attributeId == null || attrs == null) return null;
    final TypedArray typedArray = context.obtainStyledAttributes(attrs, attributeId);
    if (typedArray != null) {
      try {
        // First defined attribute
        String fontFromAttribute = typedArray.getString(0);
        if (!TextUtils.isEmpty(fontFromAttribute)) {
          return fontFromAttribute;
        }
      } catch (Exception ignore) {
        // Failed for some reason.
      } finally {
        typedArray.recycle();
      }
    }
    return null;
  }

Remember this only works if errorEnabled is set to true in the style; otherwise you need override setErrorEnabled and call setTypeface yourself.

Label View: Error Active Color

We will create a custom attribute to represent the error state for our TextInputLayout implementation:

// attrs.xml
<resources>
  <declare-styleable name="ErrorState">
    <attr format="boolean" name="state_error"/>
  </declare-styleable>
</resources>
public class MyTextInputLayout extends TextInputLayout {

  private static final int[] ERROR_STATE = new int[] { R.attr.state_error };

  private boolean errorState = false;

  ...
  
  @Override
  public void setError(@Nullable CharSequence error) {
    // We'll manage the view's error state by calls to this method, which correctly reflects when the TextInputLayout hides/shows the error text
    errorState = !TextUtils.isEmpty(error);
    refreshDrawableState();
    super.setError(error);
  }
  
  @Override
  protected int[] onCreateDrawableState(int extraSpace) {
    // add a new drawable state we are defining as error state
    int[] state = super.onCreateDrawableState(extraSpace + 1);
    if (errorState) {
      View.mergeDrawableStates(state, ERROR_STATE);
    }
    return state;
  }  
}

We update the hint text color referenced in the style for hintTextAppearance to take advantage of this new state:

// res/color/hint_color.xml
<selector xmlns:app="http://schemas.android.com/apk/res-auto">
  <item app:state_error="true" android:color="@color/error_color"/>
  ...
</selector>

Add everything up and we should be able to reproduce this result, which has the correct error text view font applied, custom error label text color, custom hint and underline colors:

new-style

And selected error view behavior:

new-style-error-select

Error Cursor Color?

Have colorControlActivated also point to a selector that takes advantage of the new error state we’ve created, similar to hint_color.

Summary

Style/theme attributes can get you most places when styling the TextInputLayout, but fixing the error text typeface and tweaking the error label color takes more effort to deliver a look and feel that you want for your app outside of stock behavior.

9 Apr 2016

I’m not going to go into all of the intricacies of the TextInputLayout API in this post, but rather describe some of the nuances to keep in mind while building Text Input Layouts into your Android application. If you’re just getting started on using this API, there’s good documentation here and here and here. After creating a few applications that contain Text Input Layouts myself, I’ve picked up on a few issues with the API and I thought I’d share those here.

TextInputLayout

When do I use a TextInputLayout?

I’m personally only using Text Input Layouts when I need to display an input form within a fragment. For instance, if you’re building a screen for allowing users to create new payment accounts with a credit card, Text Input Layouts are great for displaying helpful error feedback in a friendly and responsive way.

I’ve honestly never used the character count functionality that’s built into the layout because I feel that it detracts from the cleanliness of the EditText view. In my opinion, users shouldn’t ever care about the number of characters they’re entering unless there are hard requirements around the number of characters that they’re allowed to enter. And even then, if the user will hardly ever hit that requirement, then you should just use the input layout error functionality and not the character count.

So, what should you consider when using this API?

1. Clearing and Setting Errors

What I’ve learned from the error functionality of TextInputLayouts is that you should never make back-to-back calls to the setError() layout method for the same TextInputLayout.
For instance, don’t do this:

firstInputLayout.setError(null);
firstInputLayout.setError("This field is required");

The error that’s displayed will be the first error, which is an empty error in this case, instead of the last error that was set (i.e., “This field is required”). The reason for this happening is because the animation on the first setError() call hasn’t finished before the animation of the second call.
So, the error is actually set to “This field is required” after this, but what’s displayed is a blank error message:

TextInputLayoutError

This may be obvious after looking at the code as I’ve written it above, but when your error validation code consists of several different checks for several EditTexts (and you happen to think you should clear the errors before setting them again, like I did), it’s not as easy to track down what may be causing the issue.

You can test this out with the gist I created here: TextInputLayout Issues with setError

2. EditText colors set to black on error being cleared

Watch out for the known issue of the EditText coloring getting set to black once the error is cleared. Black isn’t actually the default color of TextInputLayouts or the color of EditTexts (it’s a darker grey). So, when it’s set to black, it calls the user’s attention to something that is in a normal state, which shouldn’t happen. It detracts from the usability of the form input.
You can read up about this issue here: Issue #203357.
Good news is that as long as you update to revision 23.3.0 of the Support Library, which was just released as of this month (April 2016), then you won’t have to worry about this one.

3. Styling TextInputLayouts

Accent Color and Error Color

You can style your TextInputLayouts with an accent color and even set the color that the layout will display when it is in an error state.
The accent color only affects the EditText that is currently in focus. The rest of your TextInputLayouts will fall back to the standard primary text color when their EditTexts are unfocused.
To do this, go to your styles.xml file and set the colorAccent for your app’s theme:

<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
    <item name="colorPrimary">@color/colorPrimary</item>
    <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
    <item name="colorAccent">@color/colorAccent</item>
</style>

Also, create a new style for your TextInputLayout error text color:

<style name="MyTextInputLayoutErrorText" parent="TextAppearance.Design.Error">
	<item name="android:textColor">@color/customErrorColor</item>
</style>

And set the errorTextAppearance attribute on your TextInputLayouts:

<android.support.design.widget.TextInputLayout
    android:id="@+id/first_input_layout"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:hint="@string/text_input_one_hint"
    app:errorEnabled="true"
    app:errorTextAppearance="@style/MyTextInputLayoutErrorText">
    <android.support.design.widget.TextInputEditText
        android:id="@+id/first_edit_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:singleLine="true"
        android:imeOptions="actionNext"
        android:nextFocusDown="@+id/second_edit_text"/>
</android.support.design.widget.TextInputLayout>

Don’t stack your TextInputLayouts too close together, because the error of your first TextInputLayout might overlap the hint of your second input layout. Generally, you only need to worry about this if you’re manipulating margins or padding for your vertically-oriented layout of TextInputLayouts. But, it’s something to always keep in mind since hints float above the EditText only when the EditText is focused (i.e., the cursor is on your EditText).

Adding two TextInputLayouts side by side

In order to accomplish this, you should use a LinearLayout to wrap the two TextInputLayouts with a horizontal orientation, because you don’t have to do anything extra to align the TextInputLayouts. Make sure to set the same weight on each of your TextInputLayouts! This way, your layouts don’t look unevenly aligned and you won’t give anyone a heart attack because of their incessant desire to have everything in their life perfectly aligned (like myself).

<LinearLayout
    android:id="@+id/input_fields"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:baselineAligned="false">
    <android.support.design.widget.TextInputLayout
        android:id="@+id/first_input_layout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:hint="@string/text_input_one_hint"
        app:errorEnabled="true"
        app:errorTextAppearance="@style/MyTextInputLayoutErrorText">
        <android.support.design.widget.TextInputEditText
            android:id="@+id/first_edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:imeOptions="actionNext"
            android:nextFocusRight="@+id/second_edit_text"/>
    </android.support.design.widget.TextInputLayout>

    <android.support.design.widget.TextInputLayout
        android:id="@+id/second_input_layout"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1"
        android:hint="@string/text_input_two_hint"
        app:errorEnabled="true" >
        <android.support.design.widget.TextInputEditText
            android:id="@+id/second_edit_text"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:singleLine="true"
            android:imeOptions="actionDone"/>
    </android.support.design.widget.TextInputLayout>
</LinearLayout>

TextInputLayout side by side

Uhhhh… What the heck is a TextInputEditText?

Don’t worry! If you’re not familiar with the TextInputEditText API, you’re not in trouble. Don’t feel like you need to go change all of your EditTexts to TextInputEditTexts now, because the TextInputEditText is nothing more than an EditText with an extra fix for a minor Input Method Editor (IME) usability feature: Extract mode.

Uhhhh… What the heck is Extract mode?

Extract mode is the mode that the keyboard editor switches to when you click on an EditText while you’re in landscape mode on a smaller device. The screen switches to a “full screen” editor view, like so:

TextInputEditText Extract Mode

As you can see, the IME is extracting the text view into a full screen view once the user clicks on the TextInputEditText.
To get back to the original point, TextInputEditText simply provides hint text while the user’s device’s IME is in Extract mode.
If you’re using an EditText instead of a TextInputEditText, the user will not see the “City” hint text that I’ve shown in the gif above.

Ok, so why didn’t Google’s Android team just add this API into the EditText API?
In the javadoc for the TextInputEditText, it describes itself as:

A special sub-class of EditText designed for use as a child of TextInputLayout

If you use the TextInputEditText in any other layout, the EditText won’t have any reference to a TextInputLayout hint, so there’s no way for the IME to know what text to display as a hint in Extract mode.
Also, keep in mind that if you’re not using TextInputLayouts, there’s no point in using the TextInputEditText API.

Понравилась статья? Поделить с друзьями:
  • Text format parsing error in line 1 unexpected end of input stream
  • Text error correction
  • Text blender как изменить
  • Texmod ошибка d oh
  • Teso ошибка 301