Principes pour améliorer l'accessibilité des applications (vues)

Concepts et implémentation de Jetpack Compose

Pour aider les utilisateurs ayant des besoins en matière d'accessibilité, le framework Android vous permet de créer un service d'accessibilité qui peut présenter le contenu des applications aux utilisateurs et les utiliser en leur nom.

Android fournit plusieurs services d'accessibilité, y compris :

Pour aider les personnes ayant des besoins en matière d'accessibilité à utiliser votre application, votre application doit suivre les bonnes pratiques décrites sur cette page, qui s'appuient sur les consignes décrites dans Rendre les applications plus accessibles.

Libellés

Il est important de fournir aux utilisateurs des libellés utiles et descriptifs pour chaque élément d'interface utilisateur interactif de votre application. Chaque libellé doit expliquer la signification et l'objectif d'un élément particulier. Les lecteurs d'écran tels que TalkBack peuvent annoncer ces libellés aux utilisateurs.

Dans la plupart des cas, vous spécifiez la description d'un élément d'interface utilisateur dans le fichier de ressources de mise en page qui contient cet élément. En règle générale, vous ajoutez des libellés à l'aide de l'attribut contentDescription, comme expliqué dans le guide Rendre les applications plus accessibles. Plusieurs autres techniques d'ajout de libellés sont décrites dans les sections suivantes.

Éléments modifiables

Lorsque vous ajoutez des libellés à des éléments modifiables, tels que EditText objets, il peut être utile d'afficher des exemples d'entrées valides au niveau de l'élément, et de rendre ces exemples disponibles pour les lecteurs d'écran. Dans ce cas, vous pouvez utiliser l'attribut android:hint, comme indiqué dans l'extrait suivant :

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

Dans ce cas, l'objet View doit avoir son attribut android:labelFor défini sur l'ID de l'élément EditText. Pour en savoir plus, consultez la section suivante.

Paires d'éléments où l'un décrit l'autre

Il est courant qu'un élément EditText ait un objet View correspondant qui décrit ce que les utilisateurs doivent saisir dans l'élément EditText. Vous pouvez indiquer cette relation en définissant l'attribut android:labelFor de l'objet View.

Voici un exemple d'ajout de libellé pour de telles paires d'éléments :

<!-- 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" ... />

Éléments d'une collection

Lorsque vous ajoutez des libellés aux éléments d'une collection, chaque libellé doit être unique. Ainsi, les services d'accessibilité du système peuvent faire référence à un seul élément à l'écran lors de l'annonce d'un libellé. Cette correspondance permet aux utilisateurs de savoir quand ils parcourent l'interface utilisateur ou lorsqu'ils déplacent le focus vers un élément qu'ils ont déjà découvert.

En particulier, incluez du texte supplémentaire ou des informations contextuelles dans les éléments des mises en page réutilisées, tels que RecyclerView les objets, afin que chaque élément enfant soit identifié de manière unique.

Pour ce faire, définissez la description du contenu dans le cadre de l'implémentation de votre adaptateur, comme indiqué dans l'extrait de code suivant :

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")
    }
}

Groupes de contenus similaires

Si votre application affiche plusieurs éléments d'interface utilisateur qui constituent un groupe naturel, tels que les informations d'une chanson ou les attributs d'un message, organisez-les dans un conteneur, qui est généralement une sous-classe de ViewGroup. Définissez l'attribut android:screenReaderFocusable de l'objet conteneur sur true et l'attribut android:focusable de chaque objet du conteneur sur false. Les services d'accessibilité peuvent ainsi présenter à la suite les descriptions des éléments internes dans une seule annonce. Cette consolidation des éléments associés aide les utilisateurs de technologies d'assistance à découvrir plus efficacement les informations à l'écran.

L'extrait suivant contient des éléments de contenu associés. L'attribut android:screenReaderFocusable de l'élément conteneur, une instance de ConstraintLayout, est donc défini sur true, et l'attribut android:focusable de chaque élément TextView interne est défini sur 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>

Étant donné que les services d'accessibilité annoncent les descriptions des éléments intérieurs en une seule fois, il est important que chaque description soit aussi courte que possible tout en transmettant correctement la signification de l'élément.

Remarque : En général, vous devez éviter de créer une description de contenu pour un groupe en agrégeant le texte de ses enfants. Cela rend la description du groupe fragile. Lorsque le texte d'un enfant change, la description du groupe peut ne plus correspondre au texte visible.

Dans un contexte de liste ou de grille, un lecteur d'écran peut consolider le texte des nœuds de texte enfants d'un élément de liste ou de grille. Il est préférable d'éviter de modifier cette annonce.

Groupes imbriqués

Si l'interface de votre application présente des informations multidimensionnelles, telles qu'une liste quotidienne d'événements de festival, utilisez l'attribut android:screenReaderFocusable sur les conteneurs de groupe internes. Ce schéma d'ajout de libellés offre un bon équilibre entre le nombre d'annonces nécessaires pour découvrir le contenu de l'écran et la longueur de chaque annonce.

L'extrait de code suivant présente une méthode d'ajout de libellés pour des groupes imbriqués dans d'autres groupes :

<!-- 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>

Titres dans le texte

Certaines applications utilisent des titres pour récapituler les groupes de texte qui apparaissent à l'écran. Si un élément View particulier représente un titre, vous pouvez indiquer son objectif aux services d'accessibilité en définissant l'attribut android:accessibilityHeading de l'élément sur true.

Les utilisateurs de services d'accessibilité peuvent choisir de naviguer d'un titre à l'autre plutôt que d'un paragraphe/mot à un autre. Cette flexibilité améliore l'expérience de navigation dans le texte.

Titres accessibles pour les volets

Sous Android 9 (niveau d'API 28) ou version ultérieure, vous pouvez fournir des titres accessibles pour les volets d'un écran. Pour des raisons d'accessibilité, un volet est une partie distincte d'une fenêtre, comme le contenu d'un fragment. Pour permettre aux services d'accessibilité de comprendre le comportement d'un volet, attribuez des titres descriptifs aux volets de votre application. Les services d'accessibilité peuvent ensuite fournir des informations plus précises aux utilisateurs lorsque l'apparence ou le contenu d'un volet changent.

Pour spécifier le titre d'un volet, utilisez l'attribut android:accessibilityPaneTitle, comme indiqué dans l'extrait de code suivant :

<!-- 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" ... />

Éléments décoratifs

Si un élément de votre UI n'existe qu'à des fins d'espacement ou d'apparence visuelle définissez son android:importantForAccessibility attribut sur "no".

Ajouter des actions d'accessibilité

Il est important de permettre aux utilisateurs de services d'accessibilité de suivre facilement tous les parcours utilisateur dans votre application. Par exemple, si un utilisateur peut balayer un élément d'une liste, cette action peut également être présentée aux services d'accessibilité afin que les utilisateurs disposent d'un autre moyen de suivre le même parcours utilisateur.

Rendre toutes les actions accessibles

Un utilisateur de TalkBack, Voice Access, ou Switch Access peut avoir besoin d'autres moyens pour suivre certains parcours utilisateur dans l'application. Par exemple, votre application peut exposer les actions associées à des gestes tels que le glisser-déposer ou les balayages d'écran de manière accessible aux utilisateurs de services d'accessibilité.

Grâce aux actions d'accessibilité, l'application offre aux utilisateurs d'autres moyens d'effectuer une action.

Par exemple, si votre application permet aux utilisateurs de balayer un élément, vous pouvez également exposer cette fonctionnalité via une action d'accessibilité personnalisée, comme suit :

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 actions 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

Il est préférable que la nouvelle classe TriSwitch hérite directement de la Switch classe. Ainsi, le framework d'accessibilité Android fournit la plupart des fonctionnalités d'accessibilité dont la classe TriSwitch a besoin :

  • Actions d'accessibilité : informations pour le système sur la manière dont les services d'accessibilité peuvent émuler chaque entrée utilisateur possible effectuée sur un objet TriSwitch. (Hérité de View.)
  • Événements d'accessibilité : informations pour les services d'accessibilité sur les différentes manières dont l'apparence d'un objet TriSwitch peut être modifiée lorsque l'écran s'actualise ou se met à jour. (Hérité de View.)
  • Caractéristiques : informations sur chaque objet TriSwitch, telles que le contenu du texte affiché. (Hérité de TextView.)
  • Informations d'état : description de l'état actuel d'un objet TriSwitch, tel que "coché" ou "non coché". (Hérité de CompoundButton.)
  • Description textuelle de l'état : explication textuelle de chaque état. (Hérité de Switch.)

Ce comportement de Switch et de ses super-classes est presque le même que celui des objets TriSwitch. Votre implémentation peut donc se concentrer sur l'augmentation du nombre d'états possibles de deux à trois.

Définir des événements personnalisés

Lorsque vous étendez un widget système, vous modifiez probablement un aspect de l'interaction des utilisateurs avec ce widget. Il est important de définir ces modifications d'interaction afin que les services d'accessibilité puissent mettre à jour le widget de votre application comme si l'utilisateur interagissait directement avec le widget.

En règle générale, pour chaque rappel basé sur une vue que vous ignorez, vous devez également redéfinir l'action d'accessibilité correspondante en ignorant ViewCompat.replaceAccessibilityAction(). Dans les tests de votre application, vous pouvez valider le comportement de ces actions redéfinies en appelant ViewCompat.performAccessibilityAction().

Fonctionnement de ce principe pour les objets TriSwitch

Contrairement à un objet Switch ordinaire, un objet TriSwitch passe par trois états possibles. Par conséquent, l'action d'accessibilité ACTION_CLICK correspondante doit être mise à jour :

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;
    }
}

Ressources supplémentaires

Pour en savoir plus sur la manière de rendre votre application plus accessible, consultez les ressources supplémentaires suivantes :

Ateliers de programmation

Articles de blog