Principios para mejorar la accesibilidad de la app

Para brindar asistencia a usuarios con necesidades de accesibilidad, el framework de Android te permite crear un servicio de accesibilidad capaz de presentar contenido de apps a los usuarios y de operarlas por ellos.

Android proporciona varios servicios de accesibilidad del sistema, incluidos los siguientes:

  • TalkBack: Ayuda a las personas con visión reducida o ciegas. Anuncia el contenido a través de una voz sintetizada y realiza acciones en una app en respuesta a los gestos del usuario.
  • Accesibilidad con interruptores: Ayuda a las personas con discapacidades motoras. Destaca elementos interactivos y realiza acciones como respuesta cuando el usuario presiona un botón. Permite controlar el dispositivo usando solo uno o dos botones.

Para ayudar a las personas con necesidades de accesibilidad a usar tu app como corresponde, esta debe seguir las prácticas recomendadas que se describen en esta página, que se basan en los lineamientos incluidos en Cómo mejorar la accesibilidad de las apps.

Cada una de estas prácticas recomendadas, que se describen en las siguientes secciones, pueden mejorar la accesibilidad de tu app.

Etiqueta los elementos
Los usuarios deben poder comprender el contenido y el propósito de cada elemento de la IU interactivo y significativo en tu app.
Agrega acciones de accesibilidad
Cuando agregas acciones de accesibilidad, les permites a los usuarios de esos servicios completar flujos críticos en tu app.
Extiende los widgets del sistema
Compila a partir de los elementos de vista que incluye el framework, en lugar de crear tus propias vistas personalizadas. Las clases de vista y widget del framework ya proporcionan la mayoría de las capacidades de accesibilidad que necesita tu app.
Utiliza señales que no hagan uso del color
Los usuarios deben poder distinguir claramente entre categorías de elementos en una IU. Para ello, usa patrones y la posición, además del color, para expresar estas diferencias.
Mejora la accesibilidad del contenido multimedia
Agrega descripciones al contenido de video o audio de tu app para que los usuarios que lo consuman no tengan que depender por completo de señales visuales o sonoras.

Etiqueta los elementos

Es importante brindarles a los usuarios etiquetas útiles y descriptivas para cada elemento de la IU interactivo de tu app. En cada etiqueta, se debe explicar el significado y el propósito de un elemento específico. Los lectores de pantalla como TalkBack pueden leerles las etiquetas a los usuarios.

En la mayoría de los casos, debes especificar la descripción de un elemento de la IU en el archivo de recursos de diseño que lo contiene. Por lo general, debes agregar etiquetas con el atributo contentDescription, como se explica en la guía sobre cómo mejorar la accesibilidad de las apps. Hay otras técnicas de etiquetado que se describen en las siguientes secciones.

Elementos editables

Cuando etiquetas elementos editables, como objetos EditText, resulta útil mostrar texto que brinde un ejemplo de entrada válida en el elemento, además de hacer que este texto de ejemplo esté disponible para los lectores de pantalla. En estos casos, puedes usar el atributo android:hint, como se muestra en el siguiente fragmento:

<!-- The hint text for en-US locale would be
     "Apartment, suite, or building". -->
<EditText
   android:id="@+id/addressLine2"
   android:hint="@string/aptSuiteBuilding" ... />

En este caso, el objeto View debe tener su atributo android:labelFor establecido en el ID del elemento EditText. Para obtener más detalles, consulta la siguiente sección.

Pares de elementos en los que uno describe al otro

Es común que un elemento EditText tenga un objeto View correspondiente que describa lo que los usuarios deben ingresar en el elemento EditText. Puedes establecer el atributo android:labelFor del objeto View para indicar esta relación.

En el siguiente fragmento, se muestra un ejemplo de cómo etiquetar estos pares de elementos:


<!-- Label text for en-US locale would be "Username:" -->
<TextView
   android:id="@+id/usernameLabel" ...
   android:text="@string/username"
   android:labelFor="@+id/usernameEntry" />

<EditText
   android:id="@+id/usernameEntry" ... />

<!-- Label text for en-US locale would be "Password:" -->
<TextView
   android:id="@+id/passwordLabel" ...
   android:text="@string/password
   android:labelFor="@+id/passwordEntry" />

<EditText
   android:id="@+id/passwordEntry"
   android:inputType="textPassword" ... />

Elementos de una colección

Cuando agregas etiquetas a los elementos de una colección, cada etiqueta debe ser única. De esta manera, los servicios de accesibilidad del sistema pueden hacer referencia a exactamente un elemento en la pantalla cuando leen una etiqueta. Esta correspondencia permite que los usuarios sepan cuándo completaron el ciclo de la IU o cuándo trasladaron el enfoque a un elemento que ya descubrieron.

En particular, debes incluir texto adicional o información contextual en los elementos dentro de diseños reutilizados, como los objetos RecyclerView, para que cada elemento secundario se pueda identificar de manera única.

Para ello, establece la descripción del contenido como parte de tu implementación del adaptador, como se muestra en el siguiente fragmento de código:

Kotlin

data class MovieRating(val title: String, val starRating: Integer)

class MyMovieRatingsAdapter(private val myData: Array<MovieRating>):
        RecyclerView.Adapter<MyMovieRatingsAdapter.MyRatingViewHolder>() {

    class MyRatingViewHolder(val ratingView: ImageView) :
            RecyclerView.ViewHolder(ratingView)

    override fun onBindViewHolder(holder: MyRatingViewHolder, position: Int) {
        val ratingData = myData[position]
        holder.ratingView.contentDescription = "Movie ${position}: " +
                "${ratingData.title}, ${ratingData.starRating} stars"
    }
}

Java

public class MovieRating {
    private String title;
    private int starRating;
    // ...
    public String getTitle() { return title; }
    public int getStarRating() { return starRating; }
}

public class MyMovieRatingsAdapter
        extends RecyclerView.Adapter<MyAdapter.MyRatingViewHolder> {
    private MovieRating[] myData;


    public static class MyRatingViewHolder extends RecyclerView.ViewHolder {
        public ImageView ratingView;
        public MyRatingViewHolder(ImageView iv) {
            super(iv);
            ratingView = iv;
        }
    }

    @Override
    public void onBindViewHolder(MyRatingViewHolder holder, int position) {
        MovieRating ratingData = myData[position];
        holder.ratingView.setContentDescription("Movie " + position + ": " +
                ratingData.getTitle() + ", " + ratingData.getStarRating() +
                " stars")
    }
}

Grupos de contenido relacionado

Si tu app muestra varios elementos de la IU que forman un grupo natural, como detalles de una canción o atributos de un mensaje, organiza estos elementos dentro de un contenedor, que generalmente es una subclase de ViewGroup. Establece el atributo android:screenReaderFocusable del objeto del contenedor en true y el atributo android:focusable de cada objeto interno en false. De esta manera, los servicios de accesibilidad pueden presentar las descripciones de contenido de los elementos internos, uno tras el otro, en un único anuncio. Esta consolidación de elementos relacionados ayuda a los usuarios de la tecnología de accesibilidad a descubrir la información que se muestra en la pantalla de manera más eficiente.

El siguiente fragmento incluye elementos de contenido que se relacionan entre sí. Por ello, el elemento de contenedor, una instancia de ConstraintLayout, tiene su atributo android:screenReaderFocusable establecido en true y cada uno de los elementos TextView internos tiene su atributo android:focusable establecido en false:

<!-- In response to a single user interaction, accessibility services announce
     both the title and the artist of the song. -->
<ConstraintLayout
    android:id="@+id/song_data_container" ...
    android:screenReaderFocusable="true">

    <TextView
        android:id="@+id/song_title" ...
        android:focusable="false"
        android:text="@string/my_song_title" />
    <TextView
        android:id="@+id/song_artist"
        android:focusable="false"
        android:text="@string/my_songwriter" />
</ConstraintLayout>

Debido a que los servicios de accesibilidad anuncian las descripciones de los elementos internos en una sola frase, es importante que cada descripción sea lo más breve posible y, al mismo tiempo, transmita el significado del elemento.

Nota: En general, debes evitar crear una descripción de contenido para un grupo agregando el texto de sus elementos secundarios. Si lo haces, la descripción del grupo será frágil y, cuando cambie el texto de un elemento secundario, es posible que la descripción del grupo ya no coincida con el texto visible.

En un contexto de lista o cuadrícula, un lector de pantalla puede consolidar el texto de los nodos de texto secundarios de un elemento de cuadrícula o de lista. Se recomienda evitar modificar este anuncio.

Grupos anidados

Si la interfaz de tu app presenta información multidimensional, como una lista diaria de eventos de un festival, usa el atributo android:screenReaderFocusable en los contenedores del grupo interno. Este esquema de etiquetas ofrece un buen equilibrio entre la cantidad de anuncios necesarios para descubrir el contenido de la pantalla y la longitud de cada anuncio.

En el siguiente fragmento, se muestra un método de grupos de etiquetado dentro de grupos de mayor tamaño:

<!-- In response to a single user interaction, accessibility services
     announce the events for a single stage only. -->
<ConstraintLayout
    android:id="@+id/festival_event_table" ... >
    <ConstraintLayout
        android:id="@+id/stage_a_event_column"
        android:screenReaderFocusable="true">

        <!-- UI elements that describe the events on Stage A. -->

    </ConstraintLayout>
    <ConstraintLayout
        android:id="@+id/stage_b_event_column"
        android:screenReaderFocusable="true">

        <!-- UI elements that describe the events on Stage B. -->

    </ConstraintLayout>
</ConstraintLayout>

Encabezados dentro de texto

Algunas apps usan encabezados para resumir grupos de texto que se muestran en la pantalla. Si un elemento View específico representa un encabezado, puedes indicar su objetivo para los servicios de accesibilidad. Para ello, establece el atributo android:accessibilityHeading del elemento en true.

Los usuarios de los servicios de accesibilidad pueden elegir navegar entre encabezados, en lugar de hacerlo entre párrafos o palabras. Esta flexibilidad mejora la experiencia de navegación del texto.

Títulos del panel de accesibilidad

En Android 9 (nivel de API 28) y versiones posteriores, puedes proporcionar títulos accesibles para los paneles de una pantalla. Para los fines de la accesibilidad, un panel es una parte de una ventana distinta a nivel visual, como el contenido de un fragmento. Si deseas que los servicios de accesibilidad comprendan cómo un panel se comporta de manera similar a una ventana, debes proporcionar títulos descriptivos para los paneles de tu app. Luego, los servicios de accesibilidad pueden proporcionar información más detallada a los usuarios cuando la apariencia o el contenido de un panel cambian.

Para especificar el título de un panel, usa el atributo android:accessibilityPaneTitle, como se muestra en el siguiente fragmento:

<!-- Accessibility services receive announcements about content changes
     that are scoped to either the "shopping cart view" section (top) or
     "browse items" section (bottom) -->
<MyShoppingCartView
     android:id="@+id/shoppingCartContainer"
     android:accessibilityPaneTitle="@string/shoppingCart" ... />

<MyShoppingBrowseView
     android:id="@+id/browseItemsContainer"
     android:accessibilityPaneTitle="@string/browseProducts" ... />

Elementos decorativos

Si un elemento de tu IU solo existe para brindar espacio visual o mejorar la apariencia, establece su atributo android:importantForAccessibility en "no".

Agrega acciones de accesibilidad

Es importante que le permitas a los usuarios de servicios de accesibilidad realizar todos los flujos de usuarios de tu app. Por ejemplo, si un usuario puede deslizar un elemento de una lista, esta acción también se puede exponer a los servicios de accesibilidad de modo que los usuarios tengan una forma alternativa de completar el mismo flujo.

Haz que todas las acciones resulten accesibles

Un usuario de TalkBack, Acceso por voz o Accesibilidad con interruptores podría necesitar formas alternativas de completar determinados flujos de usuarios dentro de la app. Para acciones asociadas con gestos como el de arrastrar y soltar o los deslizamientos, tu app puede exponer las acciones de modo que resulte accesible para los usuarios de servicios de accesibilidad.

Con las acciones de accesibilidad, la app puede ofrecer a los usuarios maneras alternativas de completar una acción.

Por ejemplo, si tu app les permite a los usuarios deslizar un elemento, también puedes exponer la funcionalidad a través de una acción de accesibilidad personalizada, como esta:

Kotlin

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive)
) { _, _ ->
    // Same method executed when swiping on itemView
    archiveItem()
    true
}

Java

ViewCompat.addAccessibilityAction(
    // View to add accessibility action
    itemView,
    // Label surfaced to user by an accessibility service
    getText(R.id.archive),
    (view, arguments) -> {
        // Same method executed when swiping on itemView
        archiveItem();
        return true;
    }
);

With the custom accessibility action implemented, users can access the action through the actions menu.

Make available actions understandable

When a view supports actions such as touch & hold, an accessibility service such as TalkBack announces it as "Double tap and hold to long press."

This generic announcement doesn't give the user any context about what a touch & hold action does.

To make this announcement more descriptive, you can replace the accessibility action’s announcement like so:

Kotlin

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
)

Java

ViewCompat.replaceAccessibilityAction(
    // View that contains touch & hold action
    itemView,
    AccessibilityNodeInfoCompat.AccessibilityActionCompat.ACTION_LONG_CLICK,
    // Announcement read by TalkBack to surface this action
    getText(R.string.favorite),
    null
);

This results in TalkBack announcing "Double tap and hold to favorite," helping users understand the purpose of the action.

Extend system widgets

Note: When you design your app's UI, use or extend system-provided widgets that are as far down Android's class hierarchy as possible. System-provided widgets that are far down the hierarchy already have most of the accessibility capabilities your app needs. It's easier to extend these system-provided widgets than to create your own from the more generic View, ViewCompat, Canvas, and CanvasCompat classes.

If you must extend View or Canvas directly, which might be necessary for a highly customized experience or a game level, see Make custom views more accessible.

This section uses the example of implementing a special type of Switch called TriSwitch while following best practices around extending system widgets. A TriSwitch object works similarly to a Switch object, except that each instance of TriSwitch allows the user to toggle among three possible states.

Extend from far down the class hierarchy

The Switch object inherits from several framework UI classes in its hierarchy:

View
↳ TextView
  ↳ Button
    ↳ CompoundButton
      ↳ Switch

Lo ideal es que la nueva clase TriSwitch se extienda directamente desde la clase Switch. De esta manera, el framework de accesibilidad de Android brinda la mayoría de las funciones de accesibilidad que necesita la clase TriSwitch:

  • Acciones de accesibilidad: Informan al sistema acerca de cómo los servicios de accesibilidad pueden emular cada entrada posible de los usuarios realizada en un objeto TriSwitch (heredado de View).
  • Eventos de accesibilidad: Informan a los servicios de accesibilidad sobre todas las formas en que las que puede cambiar la apariencia de un objeto TriSwitch cuando la pantalla se vuelve a cargar o se actualiza (heredado de View).
  • Características: Son detalles sobre cada objeto TriSwitch, como el contenido de cualquier texto que muestra (heredado de TextView).
  • Información de estado: Es la descripción del estado de un objeto TriSwitch en un determinado momento, como "marcado" o "desmarcado" (heredado de CompoundButton).
  • Descripción de texto del estado: Es una explicación basada en texto de lo que representa cada estado (heredado de Switch).

Este comportamiento de Switch y sus superclases es casi el mismo que el de los objetos TriSwitch. Por lo tanto, tu implementación puede enfocarse en expandir la cantidad de estados posibles de dos a tres.

Define eventos personalizados

Cuando extiendes un widget del sistema, es probable que cambies algún aspecto de la manera en la que los usuarios interactúan con él. Es importante definir estos cambios de interacción para que los servicios de accesibilidad puedan actualizar el widget de tu app como si el usuario interactuara directamente con él.

Como lineamiento general, para cada devolución de llamada basada en vistas que anules, debes volver a definir la acción de accesibilidad correspondiente anulando ViewCompat.replaceAccessibilityAction(). Durante las pruebas de la app, puedes llamar a ViewCompat.performAccessibilityAction() para validar el comportamiento de estas acciones redefinidas.

Cómo funcionaría este principio para los objetos TriSwitch

A diferencia de un objeto Switch común, presionar un objeto TriSwitch cambia entre tres estados posibles. Por lo tanto, la acción de accesibilidad ACTION_CLICK correspondiente debe actualizarse:

Kotlin

class TriSwitch(context: Context) : Switch(context) {
    // 0, 1, or 2
    var currentState: Int = 0
        private set

    init {
        updateAccessibilityActions()
    }

    private fun updateAccessibilityActions() {
        ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
            action-label) {
            view, args -> moveToNextState()
        })
    }

    private fun moveToNextState() {
        currentState = (currentState + 1) % 3
    }
}

Java

public class TriSwitch extends Switch {
    // 0, 1, or 2
    private int currentState;

    public int getCurrentState() {
        return currentState;
    }

    public TriSwitch() {
        updateAccessibilityActions();
    }

    private void updateAccessibilityActions() {
        ViewCompat.replaceAccessibilityAction(this, ACTION_CLICK,
            action-label, (view, args) -> moveToNextState());
    }

    private void moveToNextState() {
        currentState = (currentState + 1) % 3;
    }
}

Utiliza señales que no hagan uso del color

Para ayudar a los usuarios daltónicos, utiliza señales que no requieran el uso del color para distinguir elementos de la IU dentro de las pantallas de tu app. Algunas de estas técnicas consisten en usar diferentes formas o tamaños, mostrar patrones visuales o de texto, o agregar respuesta táctil o de audio para identificar las diferencias entre los elementos.

En la Figura 1, se muestran dos versiones de una actividad. Una versión solo usa color para distinguir entre dos acciones posibles en un flujo de trabajo. La otra versión usa la práctica recomendada de incluir formas y texto además de color para destacar las diferencias entre las dos opciones:

Figura 1: Ejemplos de creación de elementos de la IU solo con color (izquierda) y con color, formas y texto (derecha).

Mejora la accesibilidad del contenido multimedia

Si estás desarrollando una app que incluye contenido multimedia, como un clip de video o una grabación de audio, asegúrate de que los usuarios con diferentes tipos de necesidades de accesibilidad puedan comprender el material. En particular, te recomendamos que hagas lo siguiente:

  • Incluye controles que les permitan a los usuarios pausar o detener el contenido multimedia, cambiar el volumen y activar o desactivar los subtítulos.
  • Si en un video se presenta información fundamental para completar un flujo de trabajo, proporciona el mismo contenido en un formato alternativo, como una transcripción.

Recursos adicionales

Si deseas obtener más información sobre cómo mejorar la accesibilidad de la app, consulta los siguientes recursos adicionales:

Codelabs

Entradas de blog