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 :
- TalkBack: aide aux personnes souffrant d'une déficience visuelle ou non-voyantes. Il annonce le contenu à l'aide d'une voix de synthèse et effectue des actions sur une application en réponse aux gestes de l'utilisateur.
- Switch Access: aide les personnes ayant un handicap moteur. Il met en évidence les éléments interactifs et effectue des actions lorsque l'utilisateur appuie sur un bouton. Il permet de contrôler l'appareil à l'aide d'un ou deux boutons seulement.
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 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
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é deView.) - Événements d'accessibilité : informations pour les services d'accessibilité sur les différentes manières dont l'apparence d'un objet
TriSwitchpeut être modifiée lorsque l'écran s'actualise ou se met à jour. (Hérité deView.) - Caractéristiques : informations sur chaque objet
TriSwitch, telles que le contenu du texte affiché. (Hérité deTextView.) - Informations d'état : description de l'état actuel d'un objet
TriSwitch, tel que "coché" ou "non coché". (Hérité deCompoundButton.) - 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 :