1. Introduction
Les utilisateurs peuvent interagir avec votre application à l'aide d'un clavier physique, généralement sur des appareils à grand écran tels que les tablettes et les appareils ChromeOS, mais aussi sur des appareils XR. Il est important que les utilisateurs puissent naviguer dans votre application aussi efficacement avec un clavier physique qu'avec un écran tactile. De plus, lorsque vous concevez votre application pour les écrans de TV ou de voiture, qui ne disposent pas forcément d'une saisie tactile et qui reposent plutôt sur des pavés directionnels ou des encodeurs rotatifs, vous devez appliquer les mêmes principes de navigation au clavier.
Compose vous permet de gérer les saisies provenant de claviers physiques, de pavés directionnels et d'encodeurs rotatifs de manière unifiée. Un principe clé d'une bonne expérience utilisateur pour ces modes de saisie est que les utilisateurs peuvent déplacer de manière intuitive et cohérente le focus clavier vers le composant interactif avec lequel ils souhaitent interagir.
Dans cet atelier de programmation, vous allez apprendre les points suivants :
- Comment implémenter des modèles courants de gestion du focus clavier pour une navigation intuitive et cohérente
- Comment vérifier si le déplacement du focus clavier se comporte comme prévu
Prérequis
- Expérience dan la création d'applications avec Compose.
- Connaissances de base de Kotlin, y compris des lambdas et des coroutines
Objectif de l'atelier
Vous implémentez les modèles de gestion du focus clavier suivants :
- Mouvement du focus clavier : du début à la fin, de haut en bas dans le schéma en forme de Z
- Focus initial logique : sélectionnez l'élément d'interface utilisateur avec lequel l'utilisateur est susceptible d'interagir
- Restauration du focus : déplacez le focus sur l'élément d'interface utilisateur avec lequel l'utilisateur a interagi précédemment
Points abordés
- Principes de base de la gestion du focus dans Compose
- Comment créer un élément d'interface utilisateur en tant que cible de focus
- Comment demander à ce que le focus déplace un élément d'interface utilisateur
- Comment déplacer le focus clavier vers un élément d'interface utilisateur spécifique dans un groupe d'éléments d'interface utilisateur
Ce dont vous avez besoin
- Android Studio Ladybug ou version ultérieure
- L'un des appareils suivants pour exécuter l'application exemple :
- Un appareil à grand écran doté d'un clavier physique
- Un appareil virtuel Android pour les appareils à grand écran, tels que l'émulateur redimensionnable
2. Configuration
- Clonez le dépôt GitHub contenant les ateliers de programmation propres aux grands écrans :
git clone https://github.com/android/large-screen-codelabs
Vous pouvez également télécharger et désarchiver le fichier ZIP "large-screen-codelabs" :
- Accédez au dossier
focus-management-in-compose: - Dans Android Studio, ouvrez le projet. Le dossier
focus-management-in-composecontient un projet. - Si vous ne disposez pas d'une tablette Android, d'un appareil pliable ou d'un appareil ChromeOS équipé d'un clavier physique, ouvrez le Device Manager (Gestionnaire d'appareils) dans Android Studio, puis créez l'appareil Resizable (Redimensionnable) dans la catégorie Phone (Téléphone).
Figure 1. Configuration de l'émulateur redimensionnable dans Android Studio.
3. Explorer le code de démarrage
Le projet comporte deux modules :
- start : contient le code de démarrage du projet. Vous allez modifier ce code pour terminer l'atelier de programmation.
- solution : contient le code final pour cet atelier de programmation.
L'application exemple comporte trois onglets :
- Focus target (Cible de focus)
- Focus traversal order (Ordre de balayage focus)
- Focus group (Groupe focus)
L'onglet de la cible de focus s'affiche lorsque l'application est lancée.

Figure 2. L'onglet Focus target (Cible de focus) s'affiche au lancement de l'application.
Le package ui contient le code d'interface utilisateur suivant avec lequel vous interagissez :
App.kt: implémente les ongletstab.FocusTargetTab.kt: contient le code de l'onglet "Focus target" (Cible de focus)tab.FocusTraversalOrderTab.kt: contient le code de l'onglet "Focus traversal order" (Ordre de balayage focus)tab.FocusGroup.kt: contient le code de l'onglet "Focus group" (Groupe focus)FocusGroupTabTest.kt: test instrumenté pourtab.FocusTargetTab.kt(le fichier se trouve dans le dossierandroidTest)
4. Focus target (Cible de focus)
Une cible de focus est un élément d'interface utilisateur sur lequel le focus clavier peut se déplacer. Les utilisateurs peuvent déplacer le focus clavier à l'aide de la touche Tab ou des touches directionnelles (fléchées) :
- Touche
Tab: le focus se déplace vers la cible suivante ou précédente de façon unidimensionnelle. - Touches directionnelles : le focus peut se déplacer de façon bidimensionnelle : vers le haut, le bas, la gauche et la droite.
Les onglets sont des cibles de focus. Dans l'application exemple, l'arrière-plan des onglets est mis à jour visuellement lorsque le focus est sur l'onglet.

Figure 3. L'arrière-plan du composant change lorsque le focus se déplace vers l'une de ses cibles.
Les éléments d'interface utilisateur interactifs sont des cibles de focus par défaut
Un composant interactif est une cible de focus par défaut. En d'autres termes, l'élément d'interface utilisateur est une cible de focus si les utilisateurs peuvent appuyer dessus.
L'application exemple comporte trois cartes dans l'onglet Focus target (Cible de focus). Les 1er et 3e éléments sont des cibles de focus. Le 2e élément ne l'est pas. L'arrière-plan de la 3e carte est mis à jour lorsque l'utilisateur déplace le focus depuis la 1re carte avec la touche Tab.

Figure 4. Les cibles de focus de l'application excluent la 2e carte.
Modifier la deuxième carte pour qu'elle soit une cible de focus
Vous pouvez définir la 2e carte comme cible de focus en la transformant en élément d'interface utilisateur interactif. Le moyen le plus simple consiste à utiliser le modificateur clickable comme suit :
- Ouvrez
FocusTargetTab.ktdans le packagetabs. - Modifiez le composable
SecondCardavec le modificateurclickablecomme suit :
@Composable
fun FocusTargetTab(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier
) {
FirstCard(
onClick = onClick,
modifier = Modifier.width(240.dp)
)
SecondCard(
modifier = Modifier
.width(240.dp)
.clickable(onClick = onClick)
)
ThirdCard(
onClick = onClick,
modifier = Modifier.width(240.dp)
)
}
}
Exécuter l'application
L'utilisateur peut désormais sélectionner la 2e carte en plus de la 1re carte et de la 3e carte. Vous pouvez essayer sur l'onglet Focus target (Cible de focus). Vérifiez que vous pouvez déplacer le focus de la 1re carte vers la 2e carte à l'aide de la touche Tab.

Figure 5. Déplacement du focus de la 1re carte vers la 2e carte à l'aide de la touche Tab
5. Parcours du focus dans un schéma en forme de Z
Les utilisateurs s'attendent à ce que le focus clavier se déplace de gauche à droite et de haut en bas si les paramètres linguistiques sont configurés pour un système d'écriture de gauche à droite. Cet ordre de balayage est appelé schéma en forme de Z.
Toutefois, Compose ignore la mise en page lorsqu'il détermine la prochaine cible de focus de la touche Tab et utilise à la place un balayage unidimensionnel basé sur l'ordre des appels de fonction composable.
Balayage du focus unidimensionnel
L'ordre de balayage du focus unidimensionnel provient de l'ordre des appels de fonction composable plutôt que de la mise en page de l'application.
Dans l'application exemple, le focus se déplace dans l'ordre suivant dans l'onglet Focus traversal order (Ordre de balayage du focus) :
- 1re carte
- 4e carte
- 3e carte
- 2e carte

Figure 6. Le balayage du focus suit l'ordre des fonctions composables.
La fonction FocusTraversalOrderTab implémente l'Focus traversal order (Ordre de balayage du focus) de l'application exemple. La fonction appelle les fonctions conposables pour les cartes : FirstCard, FourthCard, ThirdCard et SecondCard, dans cet ordre.
@Composable
fun FocusTraversalOrderTab(
modifier: Modifier = Modifier
) {
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp)
) {
FirstCard(
onClick = onClick,
modifier = Modifier.width(240.dp)
)
FourthCard(
onClick = onClick,
modifier = Modifier
.width(240.dp)
.offset(x = 256.dp)
)
ThirdCard(
onClick = onClick,
modifier = Modifier
.width(240.dp)
.offset(y = (-151).dp)
)
}
SecondCard(
modifier = Modifier.width(240.dp)
)
}
}
Mouvement du focus dans le schéma en forme de Z
Vous pouvez intégrer le mouvement du focus en forme de Z dans l'onglet Focus traversal order (Ordre de balayage du focus) de l'application exemple en procédant comme suit :
- Ouvrir
tabs.FocusTraversalOrderTab.kt - Supprimez le modificateur de décalage des composables
ThirdCardetFourthCard. - Remplacez la disposition actuelle de l'onglet (une ligne avec deux colonnes) par une disposition à une colonne avec deux lignes.
- Déplacez les composables
FirstCardetSecondCardvers la première ligne. - Déplacez les composables
ThirdCardetFourthCardvers la deuxième ligne.
Le code modifié est le suivant :
@Composable
fun FocusTraversalOrderTab(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier
) {
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
FirstCard(
onClick = onClick,
modifier = Modifier.width(240.dp),
)
SecondCard(
onClick = onClick,
modifier = Modifier.width(240.dp)
)
}
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
ThirdCard(
onClick = onClick,
modifier = Modifier.width(240.dp)
)
FourthCard(
onClick = onClick,
modifier = Modifier.width(240.dp)
)
}
}
}
Exécuter l'application
L'utilisateur peut désormais déplacer le focus de droite à gauche et de haut en bas, selon le schéma en forme de Z. Vous pouvez essayer dans l'onglet Focus traversal order (Ordre de balayage du focus) et vérifier que le focus se déplace dans l'ordre suivant à l'aide de la touche Tab
- 1re carte
- 2e carte
- 3e carte
- 4e carte

Figure 7. Balayage du focus en forme de Z.
6. focusGroup
Le focus passe de la 1re carte à la 3e carte à l'aide de la touche directionnelle right de l'onglet Focus group (Groupe focus). Ce mouvement est probablement un peu déroutant pour les utilisateurs, car les deux cartes ne sont pas côte à côte.

Figure 8. Déplacement inattendu du focus de la 1re carte à la 3e.
Le balayage du focus bidimensionnel fait référence aux informations de mise en page
Appuyer sur une touche directionnelle déclenche un balayage du focus à deux dimensions. Il s'agit d'un déplacement de focus courant sur les téléviseurs, car les utilisateurs interagissent avec votre application à l'aide d'un pavé directionnel. Appuyer sur les touches fléchées du clavier déclenche également un balayage à deux dimensions, car elles imitent la navigation avec un pavé directionnel.
Dans le balayage du focus bidimensionnel, le système fait référence aux informations géométriques des éléments de l'interface utilisateur et détermine la cible de focus pour le déplacement. Par exemple, le focus se déplace vers la 1re carte à partir de l'onglet de la cible du focus avec la touche directionnelle down. Si vous appuyez sur la touche directionnelle vers le haut, le focus se déplace vers l'onglet Focus target (Cible de focus)

Figure 9. Balayage du focus avec les touches de direction vers le bas et vers le haut.
Le balayage du focus bidimensionnel ne se termine pas, contrairement au déplacement du focus unidimensionnel avec la touche Tab. Par exemple, l'utilisateur ne peut pas déplacer le focus avec la touche vers le bas lorsque la 2e carte est sélectionnée.

Figure 10. La touche directionnelle vers le bas ne peut pas déplacer le focus lorsque le focus est sur la deuxième carte.
Cibles focus au même niveau
Le code suivant implémente l'écran mentionné plus haut. Il existe quatre cibles de focus : FirstCard, SecondCard, ThirdCard et FourthCard. Ces quatre cibles du focus sont au même niveau, et ThirdCard est le premier élément à droite de FirstCard dans la mise en page. C'est pourquoi le focus passe de la 1re carte à la 3e carte avec la touche directionnelle right.
@Composable
fun FocusGroupTab(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier,
) {
FirstCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
) {
SecondCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
ThirdCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
FourthCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
}
}
}
Grouper les cibles de focus avec le modificateur focusGroup
Vous pouvez modifier le mouvement déroutant du focus en procédant comme suit :
- Ouvrir
tabs.FocusGroup.kt - Modifiez la fonction composable
Columndans la fonction composableFocusGroupTabavec le modificateurfocusGroup.
Le code mis à jour est le suivant :
@Composable
fun FocusGroupTab(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier,
) {
FirstCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier.focusGroup(),
) {
SecondCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
ThirdCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
FourthCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
}
}
}
Le modificateur focusGroup crée un groupe de focus composé des cibles de focus dans le composant modifié. Les cibles de focus dans le groupe de focus et celles en dehors de ce groupe sont à des niveaux différents. Aucune cible de focus n'est placée à droite du composable FirstCard. Par conséquent, le focus ne passe pas de la 1re carte à une autre carte lors d'un appui sur la touche directionnelle right.
Exécuter l'application
Désormais, le focus ne passe pas de la 1re carte à la 3e carte avec la touche directionnelle right dans l'onglet Focus group (Groupe focus) de l'application exemple.
7. Demander le focus
Les utilisateurs ne peuvent pas utiliser de clavier ni de pavé directionnel pour sélectionner des éléments d'interface utilisateur arbitraires avec lesquels interagir. Les utilisateurs doivent déplacer le focus clavier vers un composant interactif avant d'interagir avec l'élément.
Par exemple, les utilisateurs doivent déplacer le focus de l'onglet Focus target (Cible de focus) vers la 1re carte avant d'interagir avec celle-ci. Vous pouvez réduire le nombre d'actions à effectuer pour lancer la tâche principale de l'utilisateur en définissant logiquement le focus initial.

Figure 11. Trois appuis sur la touche Tab permettent de sélectionner la 1re carte.
Demander le focus avec FocusRequester
Vous pouvez demander à ce que le focus déplace un élément d'interface utilisateur avec FocusRequester. Un objet FocusRequester doit être associé à un élément d'interface utilisateur avant d'appeler la méthode requestFocus().
Définir le focus initial sur la première carte
Vous pouvez définir le focus initial sur la 1re carte en procédant comme suit :
- Ouvrir
tabs.FocusTarget.kt - Déclarez la valeur
firstCarddans la fonction composableFocusTargetTabet initialisez-la avec un objetFocusRequesterrenvoyé par la fonctionremember. - Modifiez la fonction composable
FirstCardavec le modificateurfocusRequester. - Spécifiez la valeur
firstCardcomme argument du modificateurfocusRequester. - Appelez la fonction composable
LaunchedEffectavec la valeurUnit, puis appelez la méthode requestFocus() sur la valeurfirstCarddans le lambda transmis à la fonction composableLaunchedEffect.
Un objet FocusRequester est créé et associé à un élément d'interface utilisateur à la deuxième et à la troisième étape. À la cinquième étape, il est demandé au focus de se déplacer sur l'élément d'interface utilisateur associé lorsque le composable FocusdTargetTab est composé pour la première fois.
Le code mis à jour se présente comme suit :
@Composable
fun FocusTargetTab(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
val firstCard = remember { FocusRequester() }
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier
) {
FirstCard(
onClick = onClick,
modifier = Modifier
.width(240.dp)
.focusRequester(focusRequester = firstCard)
)
SecondCard(
modifier = Modifier
.width(240.dp)
.clickable(onClick = onClick)
)
ThirdCard(
onClick = onClick,
modifier = Modifier.width(240.dp)
)
}
LaunchedEffect(Unit) {
firstCard.requestFocus()
}
}
Exécuter l'application
Désormais, le focus clavier se place sur la 1re carte de l'onglet Focus target (Cible de focus) lorsque l'onglet est sélectionné. Vous pouvez essayer en changeant d'onglet. De plus, la 1re carte est sélectionnée au lancement de l'application.

Figure 12. Le focus est placé sur la 1re carte lorsque l'onglet Focus target (Cible de focus) est sélectionné.
8. Placer le focus sur l'onglet sélectionné
Vous pouvez spécifier la cible de focus lorsque le focus clavier entre dans un groupe de focus. Par exemple, vous pouvez placer le focus sur l'onglet sélectionné lorsque l'utilisateur place le focus sur la ligne d'onglets.
Pour implémenter ce comportement, procédez comme suit :
- Ouvrez
App.kt. - Déclarez la valeur
focusRequestersdans la fonction composableApp. - Initialisez la valeur
focusRequestersavec la valeur renvoyée par la fonctionremember, qui renvoie une liste d'objetsFocusRequester. La longueur de la liste renvoyée doit être égale à celle deScreens.entries. - Associez chaque objet
FocusRequesterde la valeurfocusRequesterau composableTaben modifiant le composable "Tab" avec le modificateurfocusRequester. - Modifiez le composable "PrimaryTabRow" avec le modificateur
focusPropertieset le modificateurfocusGroup. - Transmettez un lambda au modificateur
focusPropertieset associez la propriétéonEnterà un autre lambda. - Appelez la méthode
requestFocussur le "FocusRequester", qui est indexé avec la valeurselectedTabIndexdans la valeurfocusRequesters, à partir du lambda associé à la propriétéenter.
Le code modifié se présente comme suit :
@Composable
fun App(
modifier: Modifier = Modifier,
) {
val context = LocalContext.current
val backstack = rememberNavBackStack(Tab.FocusTarget)
val selectedTabIndex = Tab.entries.indexOf(backstack.last())
val focusRequesters = remember {
List(Tab.entries.size) { FocusRequester() }
}
Column(modifier = modifier) {
PrimaryTabRow(
selectedTabIndex = selectedTabIndex,
modifier = Modifier
.focusProperties {
onEnter = {
focusRequesters[selectedTabIndex].requestFocus()
}
}
.focusGroup()
) {
Tab.entries.forEachIndexed { index, tab ->
val isSelected = selectedTabIndex == index
Tab(
selected = isSelected,
onClick = {
if (!isSelected) {
backstack.add(tab)
}
},
text = { Text(stringResource(tab.title)) },
modifier = Modifier.focusRequester(focusRequester = focusRequesters[index])
)
}
}
NavDisplay(
backStack = backstack,
entryProvider = entryProvider {
entry<Tab.FocusTarget> {
FocusTargetTab(
onClick = context::onCardClicked,
modifier = Modifier.padding(32.dp),
)
}
entry<Tab.FocusTraversalOrder> {
FocusTraversalOrderTab(
onClick = context::onCardClicked,
modifier = Modifier.padding(32.dp)
)
}
entry<Tab.FocusGroup> {
FocusGroupTab(
onClick = context::onCardClicked,
modifier = Modifier.padding(32.dp)
)
}
}
)
}
}
Vous pouvez contrôler le mouvement du focus avec le modificateur focusProperties. Dans le lambda transmis au modificateur, modifiez "FocusProperties", qui est référencé lorsque le système choisit la cible de focus lorsque les utilisateurs appuient sur la touche Tab ou sur les touches directionnelles lorsque l'élément d'interface utilisateur modifié est sélectionné.
Lorsque le focus entre dans un groupe focus, le système appelle le lambda défini sur la propriété onEnter. Vous pouvez déplacer le focus vers un élément d'interface utilisateur en demandant le focus dans le lambda.
Exécuter l'application
Désormais, le focus clavier se place sur l'onglet sélectionné lorsque l'utilisateur met le focus sur la ligne d'onglets. Pour ce faire, procédez comme suit :
- Exécuter l'application
- Sélectionnez l'onglet Focus group (Groupe de focus).
- Déplacez le focus sur la 1re carte à l'aide de la touche directionnelle
down. - Déplacez le focus à l'aide de la touche directionnelle
up.
Figure 13. Le focus se place sur l'onglet sélectionné.
9. Restauration du focus
Les utilisateurs s'attendent à pouvoir reprendre facilement une tâche lorsqu'elle est interrompue. La restauration du focus permet de reprendre après une interruption. La restauration du focus déplace le focus clavier vers l'élément d'interface utilisateur précédemment sélectionné.
L'écran d'accueil des applications de streaming vidéo est un cas d'utilisation typique de la restauration du focus. L'écran affiche plusieurs listes de contenus vidéo, comme des films d'une certaine catégorie ou des épisodes d'un programme TV. Les utilisateurs parcourent les listes et trouvent des contenus intéressants. Parfois, les utilisateurs reviennent à la liste précédemment consultée et continuent de la parcourir. Grâce à la restauration du focus, les utilisateurs peuvent continuer à parcourir la liste sans que le focus clavier ne revienne sur le dernier élément consulté.
Le modificateur "focusRestorer" restaure le focus sur un groupe de focus
Utilisez le modificateur focusRestorer pour enregistrer et restaurer le focus d'un groupe de focus. Lorsque le focus quitte le groupe de focus, il stocke une référence à l'élément précédemment sélectionné. Lorsque le focus retourne dans le groupe de focus, il revient sur l'élément précédemment sélectionné.
Intégrer la restauration du focus avec l'onglet "Focus group" (Groupe de focus)
L'onglet Focus group (Groupe de focus) de l'application exemple comporte une ligne contenant les éléments 2nd card (2e carte), 3rd card (3e carte) et 4th card (4e carte).
Figure 14. Groupe de focus contenant la 2e carte, la 3e carte et la 4e carte.
Vous pouvez intégrer la restauration du focus dans la ligne en procédant comme suit :
- Ouvrir
tab.FocusGroupTab.kt - Modifier le composable
Rowdans le composableFocusGroupTabavec le modificateurfocusRestorer. Ce modificateur doit être appelé avant le modificateurfocusGroup.
Le code modifié se présente comme suit :
@Composable
fun FocusGroupTab(
onClick: () -> Unit,
modifier: Modifier = Modifier
) {
Column(
verticalArrangement = Arrangement.spacedBy(16.dp),
modifier = modifier,
) {
FirstCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.focusRestorer()
.focusGroup(),
) {
SecondCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
ThirdCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
FourthCard(
onClick = onClick,
modifier = Modifier.width(208.dp)
)
}
}
}
Exécuter l'application
La ligne de l'onglet Focus group (Groupe de focus) est à nouveau sélectionnée. Vous pouvez essayer de la sélectionner en procédant comme suit :
- Sélectionnez l'onglet Focus group (Groupe de focus).
- Déplacez le focus sur la 1re carte.
- Placez le focus sur la 4e carte à l'aide de la touche
Tab. - Déplacez le focus sur la 1re carte avec la touche directionnelle
up. - Appuyez sur la touche
Tab.
Le focus clavier se déplace vers la 4e carte, car le modificateur focusRestorer enregistre la référence de la carte et rétablit le focus lorsque le focus clavier entre dans le groupe de focus défini sur la ligne.

Figure 15. Le focus revient à la 4e carte après un appui sur la touche directionnelle vers le haut, suivi d'un appui sur la touche Tab.
10. Écrire un test
Vous pouvez tester la gestion du focus clavier implémentée avec des tests. Compose fournit une API permettant de vérifier si le focus est sur élément d'interface utilisateur, et aussi d'effectuer des appuis sur les touches des composants de l'interface utilisateur. Pour en savoir plus, consultez l'atelier de programmation Tester dans Jetpack Compose.
Tester l'onglet "Focus target" (Cible de focus)
Dans la section précédente, vous avez modifié la fonction composable FocusTargetTab pour définir la deuxième carte comme cible du focus. Créez un test pour l'implémentation que vous avez effectuée manuellement dans la section précédente. Le test peut être écrit en suivant les étapes ci-dessous :
- Ouvrez
FocusTargetTabTest.kt. Vous allez modifier la fonctiontestSecondCardIsFocusTargetlors des prochaines étapes. - Demandez à ce que le focus se déplace vers la 1re carte en appelant la méthode
requestFocussur l'objetSemanticsNodeInteractionpour la 1re carte. - Assurez-vous que la 1re carte est sélectionnée avec la méthode
assertIsFocused(). - Appuyez sur la touche
Taben appelant la méthodepressKeyavec la valeurKey.Tabà l'intérieur du lambda transmis à la méthodeperformKeyInput. - Vérifiez si le focus clavier se déplace vers la 2e carte en appelant la méthode
assertIsFocused()sur l'objetSemanticsNodeInteractionpour la 2e carte.
Le code mis à jour se présente comme suit :
@OptIn(ExperimentalTestApi::class, ExperimentalComposeUiApi::class)
@Test
fun testSecondCardIsFocusTarget() {
composeTestRule.setContent {
LocalInputModeManager
.current
.requestInputMode(InputMode.Keyboard)
FocusTargetTab(onClick = {})
}
val context = InstrumentationRegistry.getInstrumentation().targetContext
// Ensure the 1st card is focused
composeTestRule
.onNodeWithText(context.getString(R.string.first_card))
.requestFocus()
.performKeyInput { pressKey(Key.Tab) }
// Test if focus moves to the 2nd card from the 1st card with Tab key
composeTestRule
.onNodeWithText(context.getString(R.string.second_card))
.assertIsFocused()
}
Exécuter l'application
Vous pouvez exécuter le test en cliquant sur l'icône en forme de triangle affichée à gauche de la déclaration de la classe FocusTargetTest. Pour en savoir plus, consultez la section Run tests (Exécuter des tests) de l'article Test in Android Studio (Tester dans Android Studio).

11. Félicitations
Bravo ! Vous avez appris les principes de base de la gestion du focus clavier:
- Focus target (Cible de focus)
- Balayage du focus
Vous pouvez contrôler l'ordre de balayage du focus à l'aide des modificateurs Compose suivants :
- Le modificateur
focusGroup - Le modificateur
focusProperties
Vous avez implémenté le modèle typique de l'expérience utilisateur avec un clavier physique, le focus initial et la restauration du focus. Ces modèles sont implémentés en combinant les API suivantes :
- Classe
FocusRequester - Le modificateur
focusRequester - Le modificateur
focusRestorer - Fonction composable
LaunchedEffect
L'expérience utilisateur implémentée peut être testée avec des tests d'instrumentation. Compose permet d'appuyer sur les touches et de vérifier si un SemanticsNode a le focus clavier ou non.
