Android TextInputLayoutの例

このチュートリアルでは、Android TextInputLayoutが提供する機能に詳しく触れていきます。Android TextInputLayoutは、Material Design Support Libraryに含まれるデザインコンポーネントです。

Android TextInputLayoutはLinearLayoutを拡張しています。TextInputLayoutの主な用途は、EditText(またはその派生クラス)をラップし、浮動ヒントアニメーションを有効にすることです。要点:TextInputLayoutは通常のEditTextではなく、TextInputEditTextをラップすべきです。理由?TextInputEditTextはEditTextのサブクラスであり、TextInputLayoutの子として使用するために設計されています。さらに、EditTextを使用するとEditText added is not a TextInputEditText. Please switch to using that class insteadという警告が表示されます。TextInputLayoutには、浮動ヒントラベルを表示するだけでなく、他にも多くの機能があります。

Android TextInputLayoutの機能

このチュートリアルでカバーするいくつかの機能は次のとおりです:

  1. 浮動ヒントの有効化/無効化
  2. 浮動ヒントアニメーションの有効化/無効化
  3. エラーメッセージの表示
  4. 文字カウンターの表示
  5. ユーザーが文字数制限を超えた場合に警告する
  6. 浮動ヒント、エラーラベル、文字カウンターのテキスト表示をカスタマイズする
  7. パスワードの可視性トグル

これらの機能を1つずつ見て、Android Studioプロジェクトに実装します。

Android TextInputLayoutの例: プロジェクト構造

これは単一のアクティビティアプリケーションです。レイアウト、アクティビティ、styles.xmlcolors.xmlファイル内ですべての作業を行います。まず、以下に示すようにbuild.gradleファイル内にデザインサポートライブラリの依存関係を追加します。

compile 'com.android.support:design:25.3.1'

浮動ヒントの有効化/無効化

浮動ヒントは、TextInputLayoutではデフォルトで有効になっています。無効にするには、以下の属性をタグ内に追加する必要があります: app:hintEnabled="false"。以下のxmlコードはactivity_main.xmlレイアウトからで、3つのEditTextフィールドが含まれています。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.journaldev.featuresoftextinputlayout.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">


        <android.support.design.widget.TextInputEditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            android:hint="TextInputEditText" />


        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/activity_horizontal_margin">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Floating Hint Enabled Default" />

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


        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:hintEnabled="false">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Floating Hint Disabled" />

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

</LinearLayout>
</ScrollView>

第3のEditTextフィールドでは、浮動ヒントが無効になっています。上記のコードの出力を見てみましょう:

浮動ヒントアニメーションの有効化/無効化

前の機能と同様に、浮動ヒントアニメーションはデフォルトで有効になっています。無効にするには、TextInputLayoutタグ内に次の属性を追加する必要があります。app:hintAnimationEnabled="false"以下のXMLコードは、activity_main.xmlレイアウトから取得されたもので、いずれかのケースのEditTextフィールドが含まれています。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.journaldev.featuresoftextinputlayout.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/activity_horizontal_margin">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Floating Hint Enabled Default" />

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


        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:hintAnimationEnabled="false">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Hint Animation Disabled" />

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

</LinearLayout>
</ScrollView>

上記のコードの出力は以下の通りです。注目すべき点として、2番目のEditTextフィールドはフォーカスされたときに浮動ヒントをアニメーション化しません。

ヒントTextAppearanceのスタイリング

カスタムtextColortextSizeをヒントに使用するには、次の属性を使用します:app:hintTextAppearance="@style/HintText"。 HintTextスタイルは、以下に示すようにstyles.xml内に記述されています

<style name="HintText" parent="TextAppearance.Design.Hint">
        <item name="android:textSize">16sp</item>
        <item name="android:textColor">@color/colorPrimary</item>
    </style>

以下のxmlコードは、activity_main.xmlレイアウトから取得されたもので、EditTextフィールドがヒントの表示/非表示にかかわらず含まれています。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.journaldev.featuresoftextinputlayout.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/activity_horizontal_margin">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Floating Hint Enabled" />

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


        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:hintTextAppearance="@style/HintText">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Custom Hint TextAppearance" />

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

</LinearLayout>
</ScrollView>

上記のコードの出力は以下の通りです。

文字カウンター

文字カウンターは、かなりの数のアプリケーションで使用される機能です(Twitterの文字制限を覚えていますか?)。 app:counterEnabledtrueに設定し、app:counterMaxLengthにTextInputLayout内で表示したい最大文字数を設定します。文字カウンターは、デフォルトでEditTextの下に表示されます(右下)。このチュートリアルを書いている間、位置を変更する方法はまだありません。カウンターのスタイリングは、ヒントテキストのスタイリングと似ています。今回はapp:counterTextAppearanceという属性が使用されます。プロジェクトのstyles.xmlファイルには、以下のスタイルが追加されています。

<style name="CounterText" parent="TextAppearance.Design.Counter">
        <item name="android:textSize">16sp</item>
        <item name="android:textColor">@color/my_pink</item>
    </style>

以下のxmlコードは、activity_main.xmlレイアウトから取得され、デフォルトの文字カウンターおよびカスタムのものを備えたEditTextフィールドが含まれています。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.journaldev.featuresoftextinputlayout.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:counterEnabled="true"
            app:counterMaxLength="5"
            app:hintTextAppearance="@style/HintText">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Character Counter Limit 10" />

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


        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:counterEnabled="true"
            app:counterMaxLength="5"
            app:counterTextAppearance="@style/CounterText"
            app:hintTextAppearance="@style/HintText">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Character Counter Custom TextAppearance" />

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

</LinearLayout>
</ScrollView>

上記のコードの出力は以下のとおりです。 さて、上記の出力を注意深く観察しましょう。

  • 最初のEditTextフィールドは、文字数が超過した場合に、カウンターtextColor、ヒントtextColor、およびインジケーターの色が変わります。
  • 二番目のEditTextフィールドも同様ですが、制限を超えた場合にカウンターcustom textColorおよびcustom textSizeも変更されます。

文字数の制限を超えた場合に適用するスタイルを指定するには、次に示すcounterFlow属性を使用する必要があります。

文字数カウンターオーバーフロー

先ほど見たように、文字数が制限を超えると、カウンターテキストはcounterFlowで定義された属性を使用します。これらの属性が存在しない場合は、上記の出力で見たようにデフォルトの属性に従います。次に示すapp:counterOverflowTextAppearanceというパラメータを使用する必要があります。スタイルCounterOverflowはstyles.xml内に存在します:

 <style name="CounterOverFlow" parent="TextAppearance.Design.Counter.Overflow">
        <item name="android:textSize">16sp</item>
        <item name="android:textColor">@color/my_orange</item>
    </style>

前のactivity_main.xmlレイアウトに以下のコードスニペットを追加してください。

<android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:counterEnabled="true"
            app:counterMaxLength="5"
            app:counterOverflowTextAppearance="@style/CounterOverFlow"
            app:counterTextAppearance="@style/CounterText"
            app:hintTextAppearance="@style/HintText">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="CounterOverflow CustomTextAppearance" />

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

アプリケーションをもう一度実行しましょう。

エラーラベル

app:errorEnabledtrue に設定すると、EditTextフィールドの下の条件にエラーテキストを表示できます。エラーテキストのスタイルを設定するには、app:errorTextAppearance 属性を使用し、styles.xml ファイル内に以下のコードを追加します。

<style name="ErrorText" parent="TextAppearance.Design.Error">
        <item name="android:textSize">16sp</item>
        <item name="android:textColor">@color/my_black</item>
    </style>

以下のXMLコードは、activity_main.xml レイアウトから取得したもので、デフォルトのエラーラベルとカスタムエラーラベルのEditTextフィールドが含まれています。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.journaldev.featuresoftextinputlayout.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:id="@+id/errorInputLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:counterEnabled="true"
            app:counterMaxLength="5"
            app:counterOverflowTextAppearance="@style/CounterOverFlow"
            app:counterTextAppearance="@style/CounterText"
            app:errorEnabled="true"
            app:hintTextAppearance="@style/HintText">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/errorEditText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Default Error Label" />

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


        <android.support.design.widget.TextInputLayout
            android:id="@+id/customErrorInputLayout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:counterEnabled="true"
            app:counterMaxLength="5"
            app:counterOverflowTextAppearance="@style/CounterOverFlow"
            app:counterTextAppearance="@style/CounterText"
            app:errorEnabled="true"
            app:errorTextAppearance="@style/ErrorText"
            app:hintTextAppearance="@style/HintText">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/customErrorEditText"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Custom Error Label" />

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

</LinearLayout>
</ScrollView>

エラーテキストを表示するには、MainActivity.java クラスでTextInputLayoutのインスタンスに対して setError(String) メソッドを呼び出す必要があります。

package com.journaldev.featuresoftextinputlayout;

import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.text.Editable;
import android.text.TextWatcher;

public class MainActivity extends AppCompatActivity {


    TextInputLayout errorInputLayout, customErrorInputLayout;
    TextInputEditText errorEditText, customErrorEditText;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        errorEditText = (TextInputEditText) findViewById(R.id.errorEditText);
        errorInputLayout = (TextInputLayout) findViewById(R.id.errorInputLayout);

        customErrorEditText = (TextInputEditText) findViewById(R.id.customErrorEditText);
        customErrorInputLayout = (TextInputLayout) findViewById(R.id.customErrorInputLayout);

        errorEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {

                if (s.length() > errorInputLayout.getCounterMaxLength())
                    errorInputLayout.setError("Max character length is " + errorInputLayout.getCounterMaxLength());
                else
                    errorInputLayout.setError(null);

            }
        });

        customErrorEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

            }

            @Override
            public void afterTextChanged(Editable s) {

                if (s.length() > customErrorInputLayout.getCounterMaxLength())
                    customErrorInputLayout.setError("Max character length is " + customErrorInputLayout.getCounterMaxLength());
                else
                    customErrorInputLayout.setError(null);

            }
        });


    }
}

上記のコードでは、各TextInputEditTextのインスタンスにTextChangedListenerTextWatcherを実装)を追加しています。現在の文字数がカウンターの最大制限を超えた場合、エラーラベルを表示します。エラーラベルをクリアするには、setError()内の値をnullに設定します。上記のコードが提供する出力は: 注意:テキストフィールドのインジケーターはエラーラベルと同じ色を使用します。これはcounterOverflowで設定された色を上書きし、最優先で適用されます。

パスワード表示トグル

app:passwordToggleEnabledtrueに設定すると、パスワードを表示/非表示にできます。アイコンの色を変更するにはapp:passwordToggleTintを使用します。以下のXMLコードはactivity_main.xmlレイアウトから取得され、パスワード表示トグル(デフォルトのアイコンとティント付き)用のEditTextフィールドが含まれています。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    tools:context="com.journaldev.featuresoftextinputlayout.MainActivity">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:counterEnabled="true"
            app:counterMaxLength="5"
            app:counterOverflowTextAppearance="@style/CounterOverFlow"
            app:counterTextAppearance="@style/CounterText"
            app:hintTextAppearance="@style/HintText"
            app:passwordToggleEnabled="true">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Password Visibility Toggle"
                android:inputType="textPassword" />

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


        <android.support.design.widget.TextInputLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="@dimen/activity_horizontal_margin"
            app:counterEnabled="true"
            app:counterMaxLength="5"
            app:counterOverflowTextAppearance="@style/CounterOverFlow"
            app:counterTextAppearance="@style/CounterText"
            app:hintTextAppearance="@style/HintText"
            app:passwordToggleEnabled="true"
            app:passwordToggleTint="@color/my_orange">

            <android.support.design.widget.TextInputEditText
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:hint="Password Visibility Toggle Tint"
                android:inputType="textPassword" />

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

注意app:passwordToggleDrawable を使用して、独自のカスタムアイコンをパスワード表示切り替えに使用できます。これでチュートリアルは終了です。TextInputLayout にある主要な機能をすべてカバーしました。以下のリンクから Android TextInputLayout のサンプルプロジェクト をダウンロードできます。それには上記の各コードスニペットが含まれています。

Android TextInputLayout プロジェクトをダウンロード

参考文献:Android 公式ドキュメント

Source:
https://www.digitalocean.com/community/tutorials/android-textinputlayout-example