1. Introduction
Les utilisateurs peuvent interagir avec votre application à l'aide d'un clavier physique, généralement sur les appareils à grand écran comme les tablettes et les appareils ChromeOS, mais aussi sur les 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 télévision et de voiture, qui ne disposent pas forcément d'une entrée tactile et qui reposent plutôt sur des pavés directionnels ou des encodeurs rotatifs, vous devez appliquer des principes de navigation au clavier similaires.
Compose vous permet de gérer les entrées provenant de claviers physiques, de pavés directionnels et de codeurs 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 curseur du 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 de gestion du curseur courants pour une navigation intuitive et cohérente
- Comment vérifier si le déplacement du curseur du 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 curseur du clavier suivants :
- Déplacement du curseur du clavier : du début à la fin, de haut en bas, 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 curseur : déplacez le curseur 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 curseur dans Compose
- Comment créer un élément d'interface utilisateur en tant que cible de curseur
- Comment demander à ce que le curseur déplace un élément d'interface utilisateur
- Comment déplacer le curseur du 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, tel 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-compose
contient 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. Configurer 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 du curseur)
- Focus traversal order (Ordre de balayage du curseur)
- Focus group (Groupe de sélection)
L'onglet de la cible du curseur s'affiche lorsque l'application est lancée.
Figure 2. L'onglet Cible du curseur 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 du curseur)tab.FocusTraversalOrderTab.kt
: contient le code de l'onglet "Focus traversal order" (Ordre de balayage du curseur)tab.FocusGroup.kt
: contient le code de l'onglet "Focus group" (Groupe de sélection)FocusGroupTabTest.kt
: test d'instrumentation pourtab.FocusTargetTab.kt
(le fichier se trouve dans le dossierandroidTest
)
4. Focus target (Cible du curseur)
Une cible de curseur est un élément d'interface utilisateur sur lequel le curseur peut se déplacer à l'aide du clavier. Les utilisateurs peuvent déplacer le curseur du clavier à l'aide de la touche Tab
ou des touches directionnelles (fléchées) :
- Touche
Tab
: le curseur se déplace vers la cible suivante ou précédente dans une seule dimension. - Touches directionnelles : le curseur peut se déplacer dans deux dimensions : vers le haut, le bas, la gauche et la droite.
Les onglets sont des cibles de curseur. Dans l'application exemple, l'arrière-plan des onglets est mis à jour visuellement lorsque l'onglet est sélectionné.
Figure 3. L'arrière-plan du composant change lorsque le curseur se déplace vers une cible.
Les éléments d'interface utilisateur interactifs sont des cibles du curseur par défaut
Un composant interactif est une cible par défaut. En d'autres termes, l'élément d'interface utilisateur est une cible de curseur si les utilisateurs peuvent appuyer dessus.
L'application exemple comporte trois cartes dans l'onglet Focus target (Cible du curseur). Les 1er et 3e éléments sont des cibles de curseur. 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 curseur depuis la 1re carte avec la touche Tab
.
Figure 4. Les cibles du curseur de l'application excluent la 2e carte.
Modifier la deuxième carte pour qu'elle soit une cible du curseur
Vous pouvez définir la 2e carte comme cible du curseur en la transformant en élément d'interface utilisateur interactif. Le moyen le plus simple consiste à utiliser le modificateur clickable
comme suit :
- Ouvrez
FocusTargetTab.kt
dans le packagetabs
. - Modifiez le composable
SecondCard
avec le modificateurclickable
comme 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 du curseur). Vérifiez que vous pouvez déplacer le curseur de la 1re carte vers la 2e carte à l'aide de la touche Tab
.
Figure 5. Déplacement du curseur de la 1re carte vers la 2e carte à l'aide de la touche Tab
5. Parcours du curseur dans un schéma en forme de Z
Les utilisateurs s'attendent à ce que le curseur du 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 du curseur de la touche Tab
et utilise à la place un balayage unidimensionnel basé sur l'ordre des appels de fonction composable.
Balayage du curseur unidimensionnel
L'ordre de balayage du curseur 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 curseur se déplace dans l'ordre suivant dans l'onglet Focus traversal order (Ordre de balayage du curseur) :
- 1re carte
- 4e carte
- 3e carte
- 2e carte
Figure 6. Le balayage du curseur suit l'ordre des fonctions composables.
La fonction FocusTraversalOrderTab
implémente l'Focus traversal order (Ordre de balayage du curseur) 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 curseur dans le schéma en forme de Z
Vous pouvez intégrer le mouvement du curseur en forme de Z dans l'onglet Focus traversal order (Ordre de balayage du curseur) de l'application exemple en procédant comme suit :
- Ouvrez
tabs.FocusTraversalOrderTab.kt
. - Supprimez le modificateur de décalage des composables
ThirdCard
etFourthCard
. - Remplacez la disposition de l'onglet (une ligne avec deux colonnes) par une disposition à une colonne avec deux lignes.
- Déplacez les composables
FirstCard
etSecondCard
vers la première ligne. - Déplacez les composables
ThirdCard
etFourthCard
vers 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 curseur 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 curseur) et vérifier que le curseur 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 curseur en forme de Z.
6. focusGroup
Le curseur passe de la 1re carte à la 3e carte à l'aide de la touche directionnelle right
de l'onglet Focus group (Groupe de sélection). 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 curseur de la 1re carte à la 3e.
Le balayage du curseur bidimensionnel fait référence aux informations de mise en page
Appuyer sur une touche de direction déclenche un balayage bidimensionnel. Il s'agit d'un déplacement de curseur 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 curseur 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 du curseur pour le déplacement. Par exemple, le curseur se déplace vers la 1re carte à partir de l'onglet de la cible du curseur avec la touche directionnelle down
. Si vous appuyez sur la touche directionnelle vers le haut, le curseur se déplace vers l'onglet Focus target (Cible de curseur)
Figure 9. Balayage du curseur avec les touches de direction vers le bas et vers le haut.
Le balayage du curseur bidimensionnel ne se termine pas, contrairement au déplacement du curseur unidimensionnel avec la touche Tab
. Par exemple, l'utilisateur ne peut pas déplacer le curseur 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 curseur lorsque la 2e fiche est sélectionnée.
Cibles du curseur au même niveau
Le code suivant implémente l'écran mentionné ci-dessus. Il existe quatre cibles de sélection : FirstCard
, SecondCard
, ThirdCard
et FourthCard
. Ces quatre cibles de sélection sont au même niveau, et ThirdCard
est le premier élément à droite de FirstCard
dans la mise en page. C'est pourquoi le curseur 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 du curseur avec le modificateur focusGroup
Vous pouvez modifier le mouvement déroutant du curseur en procédant comme suit :
- Ouvrez
tabs.FocusGroup.kt
. - Modifiez la fonction composable
Column
dans la fonction composableFocusGroupTab
avec 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 sélection composé des cibles du curseur dans le composant modifié. Les cibles du curseur dans le groupe de sélection et celles en dehors de ce groupe sont à des niveaux différents. Aucune cible du curseur n'est placée à droite du composable FirstCard
. Par conséquent, le curseur ne se déplace vers aucune carte de la 1re carte avec la touche directionnelle right
.
Exécuter l'application
Désormais, le curseur ne passe pas de la 1re carte à la 3e carte avec la touche directionnelle right
dans l'onglet Focus group (Groupe de sélection) de l'application exemple.
7. Demander la sélection
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 curseur du clavier vers un composant interactif avant d'interagir avec l'élément.
Par exemple, les utilisateurs doivent déplacer le curseur de l'onglet Focus target (Cible du curseur) 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 la sélection avec FocusRequester
Vous pouvez demander à ce que le curseur 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 :
- Ouvrez
tabs.FocusTarget.kt
. - Déclarez la valeur
firstCard
dans la fonction composableFocusTargetTab
et initialisez-la avec un objetFocusRequester
renvoyé par la fonctionremember
. - Modifiez la fonction composable
FirstCard
avec le modificateurfocusRequester
. - Spécifiez la valeur
firstCard
comme argument du modificateurfocusRequester
. - Appelez la fonction composable
LaunchedEffect
avec la valeurUnit
, puis appelez la méthode requestFocus() sur la valeurfirstCard
dans le lambda transmis à la fonction composableLaunchedEffect
.
Un objet FocusRequester
est créé et associé à un élément d'interface utilisateur dans les deuxième et troisième étapes. À la cinquième étape, il est demandé au curseur 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 curseur du clavier se place sur la 1re carte de l'onglet Focus target (Cible du curseur) 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 curseur est placé sur la 1re carte lorsque l'onglet Focus target (Cible du curseur) est sélectionné.
8. Placer le curseur sur l'onglet sélectionné
Vous pouvez spécifier la cible du curseur lorsque le curseur du clavier entre dans un groupe de sélection. Par exemple, vous pouvez déplacer le curseur sur l'onglet sélectionné lorsque l'utilisateur déplace le curseur sur la ligne d'onglets.
Pour implémenter ce comportement, procédez comme suit :
- Ouvrez
App.kt
. - Déclarez la valeur
focusRequesters
dans la fonction composableApp
. - Initialisez la valeur
focusRequesters
avec 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
FocusRequester
de la valeurfocusRequester
au composableTab
en modifiant le composable "Tab" avec le modificateurfocusRequester
. - Modifiez le composable "PrimaryTabRow" avec le modificateur
focusProperties
et le modificateurfocusGroup
. - Transmettez un lambda au modificateur
focusProperties
et associez la propriétéenter
à un autre lambda. - Renvoyez le "FocusRequester", qui est indexé avec la valeur
selectedTabIndex
dans 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
var selectedScreen by rememberSaveable { mutableStateOf(Screen.FocusTarget) }
val selectedTabIndex = Screen.entries.indexOf(selectedScreen)
val focusRequesters = remember {
List(Screen.entries.size) { FocusRequester() }
}
Column(modifier = modifier) {
PrimaryTabRow(
selectedTabIndex = selectedTabIndex,
modifier = Modifier
.focusProperties {
enter = {
focusRequesters[selectedTabIndex]
}
}
.focusGroup()
) {
Screen.entries.forEachIndexed { index, screen ->
Tab(
selected = screen == selectedScreen,
onClick = { selectedScreen = screen },
text = { Text(stringResource(screen.title)) },
modifier = Modifier.focusRequester(focusRequester = focusRequesters[index])
)
}
}
when (selectedScreen) {
Screen.FocusTarget -> {
FocusTargetTab(
onClick = context::onCardClicked,
modifier = Modifier.padding(32.dp),
)
}
Screen.FocusTraversalOrder -> {
FocusTraversalOrderTab(
onClick = context::onCardClicked,
modifier = Modifier.padding(32.dp)
)
}
Screen.FocusRestoration -> {
FocusGroupTab(
onClick = context::onCardClicked,
modifier = Modifier.padding(32.dp)
)
}
}
}
}
Vous pouvez contrôler le mouvement du curseur avec le modificateur focusProperties
. Dans le lambda transmis au modificateur, modifiez "FocusProperties", qui est référencé lorsque le système choisit la cible du curseur 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 vous définissez la propriété enter
, le système évalue le lambda défini sur la propriété et accède à l'élément d'interface utilisateur associé à l'objet FocusRequester
renvoyé par le lambda évalué.
Exécuter l'application
Désormais, le curseur du clavier se déplace vers l'onglet sélectionné lorsque l'utilisateur déplace le curseur vers la ligne d'onglets. Pour ce faire, procédez comme suit :
- Exécuter l'application
- Sélectionnez l'onglet Focus group (Groupe de sélection).
- Placez le curseur sur la 1re carte à l'aide de la touche directionnelle
down
. - Déplacez le curseur à l'aide de la touche directionnelle
up
.
Figure 13. Le curseur se déplace vers l'onglet sélectionné.
9. Restauration du curseur
Les utilisateurs s'attendent à pouvoir reprendre facilement une tâche lorsqu'elle est interrompue. La restauration du curseur permet de reprendre après une interruption. La restauration du curseur déplace le curseur du 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 curseur. L'écran affiche plusieurs listes de contenus vidéo, comme des films d'une catégorie ou des épisodes d'une série TV. Les utilisateurs parcourent les listes et trouvent des contenus intéressants. Parfois, les utilisateurs reviennent à la liste précédemment examinée et continuent de la parcourir. Grâce à la restauration du curseur, les utilisateurs peuvent continuer à naviguer sans que le curseur ne revienne sur le dernier élément de la liste.
Le modificateur "focusRestorer" restaure le focus sur un groupe de sélection
Utilisez le modificateur focusRestorer
pour enregistrer et restaurer le curseur d'un groupe de sélection. Lorsque le curseur quitte le groupe de sélection, il stocke une référence à l'élément précédemment sélectionné. Lorsque le curseur revient dans le groupe de sélection, il est placé sur l'élément précédemment sélectionné.
Intégrer la restauration du curseur avec l'onglet "Focus group" (Groupe de sélection)
L'onglet Focus group (Groupe de sélection) 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 sélection contenant la 2e carte, la 3e carte et la 4e carte.
Vous pouvez intégrer la restauration du curseur dans la ligne en procédant comme suit :
- Ouvrez
tab.FocusGroupTab.kt
. - Modifiez le composable
Row
dans le composableFocusGroupTab
avec le modificateurfocusRestorer
. Le 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 sélection) 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 sélection).
- Déplacez le curseur sur la 1re carte.
- Placez le curseur sur la 4e carte à l'aide de la touche
Tab
. - Déplacez le curseur sur la 1re carte avec la touche directionnelle
up
. - Appuyez sur la touche
Tab
.
Le curseur du clavier se déplace vers la 4e carte, car le modificateur focusRestorer
enregistre la référence de la carte et rétablit la sélection lorsque le curseur du clavier entre dans le groupe de sélection défini sur la ligne.
Figure 15. Le curseur revient sur la 4e carte après avoir appuyé sur la flèche vers le haut, puis sur Tab
.
10. Écrire un test
Vous pouvez tester la gestion du curseur du clavier implémentée avec des tests. Compose fournit une API permettant de vérifier si un élément d'interface utilisateur est sélectionné et 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 sélection)
Dans la section précédente, vous avez modifié la fonction composable FocusTargetTab
pour définir la deuxième carte comme cible du curseur. 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 fonctiontestSecondCardIsFocusTarget
dans les étapes suivantes. - Demandez à ce que le curseur se déplace vers la 1re carte en appelant la méthode
requestFocus
sur l'objetSemanticsNodeInteraction
pour la 1re carte. - Assurez-vous que la 1re carte est sélectionnée avec la méthode
assertIsFocused()
. - Appuyez sur la touche
Tab
en appelant la méthodepressKey
avec la valeurKey.Tab
dans le lambda transmis à la méthodeperformKeyInput
. - Vérifiez si le curseur du clavier se déplace vers la 2e carte en appelant la méthode
assertIsFocused()
sur l'objetSemanticsNodeInteraction
pour 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 triangulaire affichée à gauche de la déclaration de classe FocusTargetTest
. Pour en savoir plus, consultez la section Exécuter des tests de Tester dans Android Studio.
11. Félicitations
Bravo ! Vous avez appris les principes de base de la gestion du curseur du clavier:
- Focus target (Cible du curseur)
- Balayage du curseur
Vous pouvez contrôler l'ordre de balayage du curseur à 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 curseur. 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'effectuer des pressions sur les touches et de vérifier si un SemanticsNode
est sélectionné au clavier ou non.