Diseño Android: EditText con Material Design y TextInputLayout

Última actualización : 18/08/2016

android

La especificación de Material Design para los campos de texto recoge la utilización de las denominadas “etiquetas flotantes” (floating labels). Este elemento de usabilidad consiste en ubicar las etiquetas de los campos de texto dentro del mismo (inline) para posteriormente mostrarlas en la parte superior del campo mientras este tenga el foco o bien contenga algún valor. El objetivo es optimizar el espacio vertical de la pantalla al mostrarse la etiqueta dentro del campo del texto pero evitar a la vez que el usuario pierda la noción del contenido que corresponde al campo del texto. La siguiente animación, extraída de la aplicación de ejemplo que construiremos en el presente tutorial, muestra este comportamiento:

Android TextInputLayout

Las recomendaciones de Material Design también aconsejan mostrar los errores de validación debajo del propio campo.

Android TextInputLayout error

Si el campo de texto presenta una limitación en cuanto al número de caracteres que se pueden introducir, se recomienda mostrar un contador de caracteres.

android textinputlayout-character counter

En este tutorial utilizaremos la implementación de este elemento de diseño proporcionada por Google dentro de la librería de compatibilidad lo que permite su uso en cualquier versión de Android. Asimismo, el proyecto de ejemplo se apoya en el tutorial “Diseño Android: ActionBar con Toolbar”.

Etiquetas flotantes

En primer lugar, añadimos las librerías que necesitamos en el fichero build.gradle del módulo de nuestro proyecto en el que las vamos a utilizar, generalmente el módulo app (fichero completo).

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:24.2.0'
    compile 'com.android.support:design:24.2.0'
}

Ahora vamos a crear una Activity y su pantalla que mostrará un par de campos de texto. Cada campo de texto lo vamos a componer de la siguiente manera:

  1. Definimos un elemento de tipo TextInputLayout, responsable de dotar a nuestro campo de texto de la funcionalidad de etiqueta flotante y visualización de errores estilo Material Design
  2. Definir como hijo del TextInputLayout un componente de tipo TextInputEditText, un subtipo del componente EditText creado especialmente para ser utilizado junto a TextInputLayout. También se puede utilizar directamente un EditText lo que resulta especialmente útil si incorporamos TextInputLayout a una aplicación ya existente. En este caso, los logs nos recomendarán migrar a los nuevos TextInputEditText:
    I/TextInputLayout: EditText added is not a TextInputEditText. Please switch to using that class instead.
  3. La etiqueta se define con el atributo android:hint en el TextInputLayout o bien en el TextInputEditText

La pantalla del proyecto de ejemplo que cuenta con dos EditText queda tal y como sigue:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              xmlns:tools="http://schemas.android.com/tools"
              android:layout_width="match_parent"
              android:layout_height="match_parent"
              android:orientation="vertical"
              tools:context=".MainActivity">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="?attr/colorPrimary"
        android:elevation="@dimen/elevation_toolbar"
        android:minHeight="?attr/actionBarSize"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
        tools:ignore="UnusedAttribute"/>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="@dimen/activity_horizontal_margin"
        android:layout_marginRight="@dimen/activity_horizontal_margin"
        android:layout_marginTop="@dimen/activity_vertical_margin"
        android:descendantFocusability="beforeDescendants"
        android:focusableInTouchMode="true"
        android:orientation="vertical">

        <android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/form_margin_top"
            android:hint="@string/email">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/editTextEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="textEmailAddress"/>

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

        <android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_pass"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/password"
            android:layout_marginTop="@dimen/form_margin_top">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/editTextPassword"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="textPassword"/>

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


    </LinearLayout>


</LinearLayout>

Con esto ya tenemos la funcionalidad de etiqueta flotante en nuestros campos tal y como se muestra en la animación del principio de este tutorial. Se aplican los colores definidos de forma genérica para el tema Material Design que utiliza toda la aplicación y que es el siguiente:

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <style name="AppTheme" parent="Theme.Design.Light.NoActionBar">
        <item name="colorPrimary">@color/primary</item>
        <item name="colorPrimaryDark">@color/primaryDark</item>
        <!-- texto del input text-->
        <item name="android:textColorPrimary">@android:color/black</item>
        <!-- color del cursor, linea y etiqueta de los EditText-->
        <item name="colorAccent">@color/primary</item>
    </style>

</resources>

También podemos definir un estilo específico para aplicar sólo a los componentes EditText y/o TextInputLayout. Por ejemplo:

   <!--Etiqueta flotante-->
    <style name="AppTheme.FloatingLabel" parent="@android:style/TextAppearance">
        <item name="android:textColor">@color/teal</item>
        <item name="android:textSize">@dimen/floating_label</item>
    </style>

    <!--Campo de texto-->
    <style name="EditText" parent="AppTheme">
        <!-- cursor y línea normal-->
        <item name="colorControlNormal">@color/indigo</item>
        <!-- cursor y línea con foco-->
        <item name="colorControlActivated">@color/teal</item>
    </style>

Lo aplicamos a uno de los EditText

  <android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_email"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/form_margin_top"
            android:hint="@string/email"
            app:hintTextAppearance="@style/FloatingLabel">

            <android.support.design.widget.TextInputEditText
                android:id="@+id/editTextEmail"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:inputType="textEmailAddress"
                android:theme="@style/EditText"/>

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

TextInputLayout styles

Errores

EditText proporciona un mecanismo para mostrar errores de validación muy fácil de utilizar ya que basta con indicar el texto del error utilizando el método setError.

EditText Error

Para seguir las recomendaciones de Material Design y mostrar el mensaje de error debajo del EditText simplemente hay que utilizar el método setError del TextInputLayout. Sin embargo, para borrar el mensaje de error no basta con invocar a setError(null) puesto que el espacio que ocupa el mensaje de error en pantalla sigue mostrándose. Para solventar esta situación haremos uso del método setErrorEnabled.

En el proyecto de ejemplo se ha añadido un botón para validar los campos mediante el siguiente método.

  public void validate(View view) {
    String mailError = null;
    if (TextUtils.isEmpty(editTextEmail.getText())) {
      mailError = getString(R.string.mandatory);
    }
    toggleTextInputLayoutError(textInputEmail, mailError);

    String passError = null;
    if (TextUtils.isEmpty(editTextPassword.getText())) {
      passError = getString(R.string.mandatory);
    }
    toggleTextInputLayoutError(textInputPassword, passError);

    clearFocus();
  }

  /**
   * Display/hides TextInputLayout error.
   *
   * @param msg the message, or null to hide
   */
  private static void toggleTextInputLayoutError(@NonNull TextInputLayout textInputLayout,
                                                String msg) {
    textInputLayout.setError(msg);
    if (msg == null) {
      textInputLayout.setErrorEnabled(false);
    } else {
      textInputLayout.setErrorEnabled(true);
    }
  }

TextInputLayout error

El estilo del mensaje de error puede ser personalizado fácilmente. Por ejemplo:

<!-- mensaje de error debajo del EditText-->
    <style name="AppTheme.EditTextError" parent="@android:style/TextAppearance">
        <item name="android:textColor">@color/orange</item>
        <item name="android:textSize">@dimen/error</item>
    </style>
 <android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_pass"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/form_margin_top"
            android:hint="@string/password"
            app:errorTextAppearance="@style/AppTheme.EditTextError">

TextInputLayout custom error

Contador de caracteres

Otra funcionalidad interesante de TextInputLayout es el contador de caracteres. Para utilizarlo tenemos que habilitarlo en el TextInputLayout e indicar el número máximo de caracteres que queremos permitir para el campo.

 <android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_char"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/text"
            app:counterEnabled="true"
            app:counterMaxLength="10">

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

Al superarse el límite el campo se mostrará como erróneo.

android textinputlayout-character counter

Este estilo puede ser personalizado utilizando la propiedad counterOverflowTextAppearance:

<style name="AppTheme.CharacterCounter" parent="TextAppearance.AppCompat.Small">
        <item name="android:textColor">@color/teal</item>
    </style>
<android.support.design.widget.TextInputLayout
            android:id="@+id/text_input_layout_char"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:hint="@string/text"
            app:counterEnabled="true"
            app:counterMaxLength="10"
            app:counterOverflowTextAppearance="@style/AppTheme.CharacterCounter">

Conmutador de contraseña

En la versión 24.2.0 de las Support Libraries Google ha añadido la posibilidad de “conmutar” en los campos de tipo contraseña la ocultación de caracteres tal y como recoge Material Design. Hasta ahora para dotar a nuestros campos de contraseñas de esta posibilidad teníamos que recurrir a librerías de terceros.

material design android password toggle

Esta funcionalidad ya está habilitada por omisión para todos los EditText de tipo textPassword por lo que no hay que hacer nada para utilizarla (siempre y cuando, claro está, utilicemos la versión 24.2.0 o superior). Si queremos deshabilitarla, basta con indicarlo en el TextInputLayout con el siguiente atributo:

app:passwordToggleEnabled="false"

El siguiente video muestra el resultado final del proyecto de ejemplo sin el conmutador de contraseña


Código de ejemplo

El proyecto completo se encuentra disponible en GitHub. Para más información sobre cómo utilizar GitHub, consultar este artículo.

One Response to Diseño Android: EditText con Material Design y TextInputLayout

  1. victor manuel dice:

    muy buen aporte me resolto muy beneficioso gracias viejo. una pregunta en su tutorial tiene como habilitar el botn de like y q funcione el contador y segundo q pena, como puedo enviar una copia de un mensaje de contacto al correo del usuario

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: