154
127

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

TextInputLayout の Tips 5 選

Last updated at Posted at 2016-01-01

TextInputLayout とは

TextInputLayoutAndroid Design Support Library に含まれている、テキスト入力機能を提供する View です。マテリアルデザインの Text Fields によく従っているので、マテリアルデザインを実現したいときに重宝します。


(Text fields - Components - Google design guidelines より)

しかしながら、最新の v23.1.1 においても、知らないとハマってしまう箇所がいくつかあります。

この記事では、TextInputLayout を使う際に気を付けた方が良い点について解説します。

Tips

1. 子供の EditText にも id を振った方が良い

TextInputLayout は以下のように、EditText を入れ子にして使います。

activity_main.xml
    <android.support.design.widget.TextInputLayout
        android:id="@+id/text_input_message"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <EditText
            android:id="@+id/message"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/some_hints"
            android:inputType="text"
            android:minHeight="@dimen/text_caption"
            android:textSize="@dimen/text_caption" />

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

このとき、 TextInputLayout#getEditText() で、子供の EditText が取得できるので、わざわざ EditText に id を振る必要はないように見えます。そして、実際、なくても動くように見えます。

しかし、画面を回転させてみましょう。入力途中のテキストが消えるはずです。

これは何故かというと、Android SDK が自動的に View の状態を保存/復元するときに、View が ID を持っているかどうかが判断の基準になるからです。Android Developers にも、以下のようにしっかり書かれています。

: Android システムがアクティビティのビューの状態を復元できるようにするためには、各ビューは固有の ID を持っている必要があります。これは android:id によって提供されます。

(アクティビティを再作成する | Android Developers より)

ということで、使わなくても、内部の EditText には android:id を指定しておきましょう。

余談ですが、この ID の有無をチェックしているコードは https://github.com/android/platform_frameworks_base/blob/android-6.0.0_r7/core/java/android/view/View.java#L14740 にあります。筆者は、デバッグ実行して、ここに辿りついて、ようやく上記の仕様を思い出しました(Lint で Warning を出してほしい)。

2. ヒントをアニメーションさせたくない時は

TextInputLayout は、忠実にマテリアルデザインの floating labels を実現するために、テキストボックスに入力を始めると、ヒントがキャプションにふわっと移動する、という Android にしては珍しく気の利いた機能が実装されています。

screencast 2015-12-31 07-56-36.gif

しかし、たまに邪魔です。

このアニメーションをオフにするメソッドは提供されていませんが、以下のようにプログラマティックに子供の EditText にヒントを指定すれば実現できます。ちなみに、これに関しては AOSP に Issue も開かれています

MainActivity.java
    final TextInputLayout til = (TextInputLayout) findViewById(R.id.text2);
    final CharSequence hint = til.getHint();
    til.setHint(null);
    til.getEditText().setHint(hint);
screencast 2015-12-31 08-08-02.gif

3. TextInputLayout と baseline を合わせるには

注意! Support Library 24.2.0 から、 TextInputLayoutLinearLayout を継承しないため、ここで書いてある Tip は使えないようです(参照: https://code.google.com/p/android/issues/detail?id=223772)。
@litmon さん、情報ありがとうございました。

下記の内容は参考までに残しておきますが、Support Library 24.2.0 以降では動かないことにご注意ください

通常、EditTextTextView を水平方向に並べるときは RelativeLayoutandroid:layout_alignBaseline 属性などを使うことで、テキストのベースラインが揃うようにします。

しかし、TextInputLayout が絡むと、簡単にはいきません。なぜなら、親の TextInputLayoutandroid:layout_alignBaseline 属性を指定しても、子供の EditText のベースラインには効かないからです :cry:

そういうときは以下のように指定します。TextInputLayoutLinearLayout を継承しているので、この android:baselineAlignedChildIndex 属性が使えるのです。

activity_main.xml
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:text="パイント" />

    <android.support.design.widget.TextInputLayout
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:baselineAlignedChildIndex="0"
        android:layout_alignBaseline="@id/label"
        android:layout_toLeftOf="@id/label">
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="飲んだビールの量" />
    </android.support.design.widget.TextInputLayout>

4. カウンターやエラーメッセージの見た目を変更するには

カウンターやエラーメッセージのスタイルは以下のように変更することができます。

activity_main.xml
    <android.support.design.widget.TextInputLayout
        android:id="@+id/text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        app:counterEnabled="true"
        app:counterMaxLength="10"
        app:counterTextAppearance="@style/counterText"
        app:counterOverflowTextAppearance="@style/counterOverflowText"
        app:errorTextAppearance="@style/errorText">
        <EditText
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />
    </android.support.design.widget.TextInputLayout>
values/styles.xml
    <style name="counterText">
        <item name="android:textColor">@android:color/holo_green_dark</item>
    </style>

    <style name="counterOverflowText">
        <item name="android:textStyle">bold</item>
    </style>

    <style name="errorText">
        <item name="android:textColor">@android:color/holo_red_dark</item>
        <item name="android:textStyle">bold</item>
    </style>

なお、TextInputLayout の実装を読むと、エラーテキストとカウンターは以下のような LinearLayout で表される mIndicatorArea の子要素として動的に追加されるようです(**注:**生成される View がどういうものか表すための概念としての XML です。実際にこの XML が使われるわけではありません)。

    <LinearLayout
        android:id="@+id/indicatorArea"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="horizontal">
        <TextView
            android:id="@+id/errorView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />
        <Space
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_weight="1"/>
        <TextView
            android:id="@+id/counterView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:maxLines="1"/>
    </LinearLayout>

互いの表示/非表示に関わらず、エラーメッセージが左寄せ、カウンターが右寄せになることが分かります。

5. エラーメッセージ非表示時に View の高さが戻らない問題

複数行にまたがるエラーメッセージを setError(CharSequence) で表示したあとに setError(null) を呼ぶと、エラーメッセージの View の visibility が View.INVISIBLE になりますが、 View 自体は削除されないため、下部に無駄なスペースが残ってしまいます。

これを元に戻すには setErrorEnabled(false) を呼びます。これによってエラーメッセージの View の visibility が View.GONE になります。

MainActivity.java
        final TextInputLayout til = (TextInputLayout) findViewById(R.id.text2);
        til.getEditText().addTextChangedListener(new TextWatcher() {
            @Override
            public void afterTextChanged(Editable s) {
                if (s.toString().contains("error")) {
                    til.setError("WRYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYYY!");
                } else {
                    til.setErrorEnabled(false);
                }
            }

おわりに

Design Support Library で提供される TextInputLayout を使う際に注意する点や tips を紹介しました。

ググると分かりますが、サポートライブラリのバージョンによって挙動が異なるようです。たとえば、Working with the EditText · codepath/android_guides Wiki ではカウンタの表示が左寄せですが、最新の v23.1.1 では右寄せになっています。使う際には、そのあたりにも注意が必要になるかもしれません。

参考

154
127
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
154
127

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?