Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

TextInputLayout

Макет 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>  

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


<android.support.design.widget.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" />

</android.support.design.widget.TextInputLayout>

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

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


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

<android.support.design.widget.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"/>
</android.support.design.widget.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 = (TextInputLayout) findViewById(R.id.textInputLayout);
        mEditText = (EditText) 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.


<android.support.design.widget.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" />

</android.support.design.widget.TextInputLayout>

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

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


<android.support.design.widget.TextInputLayout
    ...
    app:counterEnabled="true"
    app:counterMaxLength="140">

    <EditText
        .../>

</android.support.design.widget.TextInputLayout>

Counter TextInputLayout

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


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

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


<android.support.design.widget.TextInputLayout
    ...
    app:counterOverflowTextAppearance="@style/MyCounterText"
    app:counterEnabled="true"
    app:counterMaxLength="10">

TextInputEditText

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


<android.support.design.widget.TextInputLayout
    ...>

    <android.support.design.widget.TextInputEditText
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:hint="Введите текст"/>

</android.support.design.widget.TextInputLayout>

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

TextInputEditText

Реклама