Les Chromebooks offrent aux utilisateurs de nombreuses options de saisie : clavier, souris, pavé tactile, écran tactile, stylet, MIDI et manette de jeu/contrôleurs Bluetooth. Cela signifie que le même appareil peut devenir la station d'un DJ, le canevas d'un artiste ou la plate-forme préférée d'un joueur pour les jeux AAA en streaming.
En tant que développeur, cela vous permet de créer des expériences d'application polyvalentes et intéressantes pour vos utilisateurs, en tirant parti des périphériques d'entrée dont ils disposent déjà, qu'il s'agisse d'un clavier connecté, d'un stylet ou d'une manette de jeu Stadia. Toutefois, toutes ces possibilités vous obligent également à réfléchir à votre UI pour que l'expérience de votre application soit fluide et logique. Cela est particulièrement vrai si votre application ou votre jeu ont été conçus pour les téléphones mobiles. Par exemple, si votre jeu dispose d'un joystick tactile à l'écran pour les téléphones, vous voudrez probablement le masquer lorsqu'un utilisateur joue avec le clavier.
Sur cette page, vous trouverez les principaux problèmes à prendre en compte lorsque vous envisagez d'utiliser plusieurs sources d'entrée, ainsi que des stratégies pour les résoudre.
Découverte par l'utilisateur des modes de saisie compatibles
Idéalement, votre application répondra de manière fluide à n'importe quelle entrée choisie par l'utilisateur. Souvent, c'est simple et vous n'avez pas besoin de fournir d'informations supplémentaires à l'utilisateur. Par exemple, un bouton doit fonctionner si l'utilisateur clique dessus avec une souris, un trackpad, l'écran tactile, un stylet, etc. Vous n'avez pas besoin de lui indiquer qu'il peut utiliser ces différents appareils pour activer le bouton.
Toutefois, dans certaines situations, la méthode de saisie peut améliorer l'expérience utilisateur. Il peut donc être judicieux de l'informer que votre application la prend en charge. Voici quelques exemples :
- Une application de lecture multimédia peut prendre en charge de nombreux raccourcis clavier pratiques que l'utilisateur ne peut pas deviner facilement.
- Si vous avez créé une application de DJ, l'utilisateur peut utiliser l'écran tactile au début et ne pas se rendre compte que vous lui avez permis d'utiliser son clavier/pavé tactile pour accéder de manière tactile à certaines fonctionnalités. De même, il se peut qu'ils ne se rendent pas compte que vous prenez en charge un certain nombre de contrôleurs MIDI pour DJ. Les encourager à consulter le matériel compatible pourrait leur offrir une expérience de DJing plus authentique.
- Votre jeu peut être excellent avec un écran tactile et un clavier/une souris, mais les utilisateurs ne savent peut-être pas qu'il est également compatible avec un certain nombre de manettes de jeu Bluetooth. En connectant l'un de ces comptes, vous pourriez améliorer la satisfaction et l'engagement des utilisateurs.
Vous pouvez aider les utilisateurs à découvrir les options de saisie en leur envoyant des messages au moment opportun. L'implémentation sera différente pour chaque application. Voici quelques exemples :
- Pop-ups de première exécution ou de conseil du jour
- Les options de configuration dans les panneaux de paramètres peuvent indiquer passivement aux utilisateurs qu'une assistance existe. Par exemple, un onglet "Manette de jeu" dans le panneau des paramètres d'un jeu indique que les manettes sont prises en charge.
- Messages contextuels. Par exemple, si vous détectez un clavier physique et que l'utilisateur clique sur une action à l'aide d'une souris ou d'un écran tactile, vous pouvez afficher un conseil utile suggérant un raccourci clavier.
- Listes de raccourcis clavier. Lorsqu'un clavier physique est détecté, indiquer dans l'UI comment afficher la liste des raccourcis clavier disponibles permet à la fois de signaler la prise en charge du clavier et de fournir aux utilisateurs un moyen simple de voir et de mémoriser les raccourcis compatibles.
Libellé/Mise en page de l'UI pour la variante d'entrée
Idéalement, votre interface visuelle ne devrait pas avoir besoin de changer beaucoup si un autre périphérique d'entrée est utilisé. Toutes les entrées possibles devraient "simplement fonctionner". Cependant, il existe des exceptions importantes. Deux d'entre elles sont l'UI spécifique au toucher et les invites à l'écran.
Interface utilisateur spécifique au tactile
Chaque fois que votre application comporte des éléments d'interface utilisateur spécifiques au toucher (par exemple, un joystick à l'écran dans un jeu), vous devez réfléchir à l'expérience utilisateur lorsque le toucher n'est pas utilisé. Dans certains jeux mobiles, une partie importante de l'écran est occupée par les commandes nécessaires pour jouer de manière tactile, mais inutiles si l'utilisateur joue avec une manette ou un clavier. Votre application ou votre jeu doivent fournir une logique permettant de détecter la méthode de saisie activement utilisée et d'ajuster l'UI en conséquence. Pour obtenir des exemples, consultez la section Implémentation ci-dessous.
Invites à l'écran
Votre application peut fournir des invites utiles à vos utilisateurs. Veillez à ce qu'elles ne dépendent pas du périphérique d'entrée. Exemple :
- Balayer pour…
- Appuyez n'importe où pour fermer
- Pincer pour zoomer
- Appuyez sur X pour…
- Appuyer de manière prolongée pour activer
Certaines applications peuvent ajuster leur formulation pour être indépendantes de la méthode de saisie. Cette approche est préférable lorsque cela a du sens, mais dans de nombreux cas, la spécificité est importante. Vous devrez peut-être afficher différents messages en fonction de la méthode de saisie utilisée, en particulier dans les modes de type tutoriel, comme lors de la première exécution des applications.
Remarques sur les langues multiples
Si votre application est compatible avec plusieurs langues, vous devez réfléchir à l'architecture de vos chaînes. Par exemple, si vous acceptez trois modes de saisie et cinq langues, cela peut signifier 15 versions différentes pour chaque message de l'UI. Cela augmentera la quantité de travail nécessaire pour ajouter de nouvelles fonctionnalités et augmentera la probabilité de fautes d'orthographe.
Une approche consiste à considérer les actions d'interface comme un ensemble de chaînes distinct. Par exemple, si vous définissez l'action "Fermer" comme sa propre variable de chaîne avec des variantes spécifiques à l'entrée, comme "Appuyez n'importe où pour fermer", "Appuyez sur Échap pour fermer", "Cliquez n'importe où pour fermer" ou "Appuyez sur n'importe quel bouton pour fermer", tous les messages de votre UI qui doivent indiquer à l'utilisateur comment fermer quelque chose peuvent utiliser cette seule variable de chaîne "Fermer". Lorsque la méthode de saisie change, il vous suffit de modifier la valeur de cette variable.
Saisie au clavier virtuel / IME
N'oubliez pas que si un utilisateur utilise une application sans clavier physique, la saisie de texte peut s'effectuer à l'aide d'un clavier à l'écran. Veillez à vérifier que les éléments d'interface utilisateur nécessaires ne sont pas masqués lorsqu'un clavier à l'écran s'affiche. Pour en savoir plus, consultez la documentation sur la visibilité de l'IME Android.
Implémentation
Dans la plupart des cas, les applications ou les jeux doivent répondre correctement à toutes les entrées valides, quel que soit le contenu affiché à l'écran. Si un utilisateur utilise l'écran tactile pendant 10 minutes, puis passe soudainement au clavier, la saisie au clavier doit fonctionner, quels que soient les invites visuelles ou les commandes à l'écran. En d'autres termes, la fonctionnalité doit être prioritaire par rapport aux éléments visuels/au texte.Cela permet de garantir que votre application/jeu sera utilisable même si votre logique de détection des entrées comporte une erreur ou si une situation inattendue se produit.
L'étape suivante consiste à afficher l'interface utilisateur appropriée pour la méthode de saisie actuellement utilisée. Comment détecter cela avec précision ? Que se passe-t-il si les utilisateurs passent d'une méthode de saisie à une autre lorsqu'ils utilisent votre application ? Que se passe-t-il si l'utilisateur utilise plusieurs méthodes à la fois ?
Machine à états priorisée en fonction des événements reçus
Une approche consiste à suivre l'état d'entrée actif actuel, qui représente les invites d'entrée actuellement affichées à l'écran pour l'utilisateur. Pour ce faire, vous devez suivre les événements d'entrée réels reçus par l'application et passer d'un état à l'autre à l'aide d'une logique priorisée.
Priorité
Pourquoi donner la priorité aux états d'entrée ? Les utilisateurs interagissent avec leurs appareils de différentes manières, et votre application doit prendre en charge leur choix. Par exemple, si un utilisateur choisit d'utiliser l'écran tactile et une souris Bluetooth en même temps, cela doit être possible. Mais quels contrôles et invites de saisie à l'écran devez-vous afficher ? Souris ou tactile ?
Pour prendre cette décision de manière cohérente, vous pouvez définir chaque ensemble de requêtes comme un "état d'entrée", puis les hiérarchiser.
L'état est déterminé par les événements d'entrée reçus.
Pourquoi n'agir que sur les événements d'entrée reçus ? Vous pensez peut-être pouvoir suivre les connexions Bluetooth pour savoir si une manette Bluetooth est connectée ou surveiller les connexions USB pour les appareils USB. Cette approche n'est pas recommandée pour deux raisons principales.
Tout d'abord, les informations que vous pouvez deviner sur les appareils connectés en fonction des variables d'API ne sont pas cohérentes, et le nombre d'appareils Bluetooth/matériels/stylus ne cesse de croître.
La deuxième raison d'utiliser les événements reçus au lieu de l'état de la connexion est que les utilisateurs peuvent avoir une souris, une manette Bluetooth, une manette MIDI, etc. connectées, mais ne pas les utiliser activement pour interagir avec votre application ou votre jeu.
En répondant aux événements d'entrée qui ont été activement reçus par votre application, vous vous assurez de répondre aux actions réelles de vos utilisateurs en temps réel, et de ne pas essayer de deviner leurs intentions avec des informations incomplètes.
Exemple : jeu compatible avec les commandes tactiles, le clavier/la souris et la manette
Imaginez que vous ayez développé un jeu de course automobile pour les téléphones mobiles tactiles. Les joueurs peuvent accélérer, ralentir, tourner à droite, tourner à gauche ou utiliser le nitro pour gagner en vitesse.
L'interface à écran tactile actuelle se compose d'un joystick à l'écran en bas à gauche pour contrôler la vitesse et la direction, et d'un bouton en bas à droite pour le nitro. L'utilisateur peut collecter des bidons de nitro sur la piste. Lorsque la barre de nitro en bas de l'écran est pleine, un message s'affiche au-dessus du bouton : "Appuyez pour la nitro !". Si c'est la première partie de l'utilisateur ou si aucune saisie n'est effectuée pendant un certain temps, un texte de tutoriel s'affiche au-dessus du joystick pour montrer à l'utilisateur comment faire bouger la voiture.
Vous souhaitez ajouter la prise en charge du clavier et des manettes de jeu Bluetooth. Par où commencer ?
États d'entrée
Commencez par identifier tous les états d'entrée dans lesquels votre jeu peut s'exécuter, puis listez tous les paramètres que vous souhaitez modifier dans chaque état.
| Interactions tactiles | Clavier/Souris | Manette de jeu | |
|---|---|---|---|
|
Réactions à |
Toutes les entrées |
Toutes les entrées |
Toutes les entrées |
|
Commandes à l'écran |
- Joystick à l'écran |
- Pas de joystick |
- Pas de joystick |
|
Texte |
Appuyez pour Nitro ! |
Appuyez sur "N" pour Nitro ! |
Appuyez sur A pour Nitro ! |
|
Tutoriel |
Image du joystick pour la vitesse/la direction |
Image des touches fléchées et de WASD pour la vitesse/la direction |
Image d'une manette de jeu pour la vitesse/la direction |
Suivez l'état de l'entrée active, puis mettez à jour l'UI si nécessaire, en fonction de cet état.
Remarque : N'oubliez pas que votre jeu/application doit répondre à toutes les méthodes de saisie, quel que soit l'état. Par exemple, si un utilisateur conduit la voiture avec l'écran tactile, mais appuie sur N sur le clavier, l'action nitro doit être déclenchée.
Changements d'état prioritaires
Certains utilisateurs peuvent utiliser l'écran tactile et le clavier en même temps. Certains utilisateurs peuvent commencer à jouer à votre jeu ou à utiliser votre application sur le canapé en mode tablette, puis passer à l'utilisation du clavier sur la table. D'autres peuvent connecter ou déconnecter des manettes de jeu en cours de partie.
Dans l'idéal, vous ne devez pas avoir d'éléments d'interface utilisateur incorrects, par exemple en demandant à l'utilisateur d'appuyer sur la touche N alors qu'il utilise l'écran tactile. En même temps, dans le cas où les utilisateurs utilisent simultanément plusieurs périphériques d'entrée, comme un écran tactile et un clavier, vous ne voulez pas que l'UI bascule constamment entre les deux états.
Pour gérer ce problème, vous pouvez établir des priorités pour les types d'entrée et intégrer un délai entre les changements d'état. Pour le jeu de voiture ci-dessus, vous devez toujours vous assurer que le joystick à l'écran est visible chaque fois que des événements tactiles sont reçus, sinon le jeu peut sembler inutilisable pour l'utilisateur. L'écran tactile deviendrait alors l'appareil prioritaire.
Si des événements de clavier et d'écran tactile sont reçus simultanément, le jeu doit rester en mode écran tactile, tout en continuant à réagir aux entrées clavier. Si aucune saisie tactile n'a été reçue après cinq secondes et que des événements de clavier sont toujours reçus, les commandes à l'écran peuvent s'estomper et le jeu peut passer à l'état du clavier.
L'entrée de la manette de jeu suivrait un schéma similaire : l'état de l'UI de la manette aurait une priorité inférieure à celle du clavier/de la souris et du toucher, et n'apparaîtrait que si une entrée de la manette de jeu était reçue, et non d'autres formes d'entrée. Le jeu répondait toujours aux commandes de la manette.
Vous trouverez ci-dessous un diagramme d'état et un tableau de transition pour l'exemple. Adaptez l'idée à votre application ou jeu.
| #1 Écran tactile | #2 Clavier | #3 Gamepad | |
|---|---|---|---|
|
Déplacer vers n° 1 |
N/A |
- Saisie tactile reçue |
- Saisie tactile reçue |
|
Passez à l'étape 2 |
- Aucun contact pendant 5 s |
N/A |
- Saisie au clavier reçue |
|
Passez à l'étape 3 |
- Aucun contact pendant cinq secondes |
- Aucun clavier pendant 5 s |
N/A |
Remarque : Notez comment la hiérarchisation permet de déterminer clairement quel type d'entrée doit être dominant. L'état d'entrée passe instantanément à un niveau de priorité supérieur :
3. Manette de jeu > 2. Clavier -> 1. Interactions tactiles
dès qu'un appareil de priorité supérieure est utilisé, mais il descend lentement en priorité, uniquement après un délai et uniquement si l'appareil de priorité inférieure est activement utilisé.
Événements de saisie
Voici un exemple de code montrant comment détecter les événements d'entrée provenant de différents types de périphériques d'entrée à l'aide des API Android standards. Utilisez ces événements pour piloter votre machine à états, comme ci-dessus. Vous devez adapter le concept général aux types d'événements d'entrée que vous attendez et à votre application ou jeu.
Boutons du clavier et de la manette
// Drive the state machine based on the received input type // onKeyDown drives the state machine, but does not trigger game actions // Both keyboard and game controller events come through as key events override fun onKeyDown(keyCode: Int, event: KeyEvent?): Boolean { if (event != null) { // Check input source val outputMessage = when (event.source) { SOURCE_KEYBOARD -> { MyStateMachine.KeyboardEventReceived() "Keyboard event" } SOURCE_GAMEPAD -> { MyStateMachine.ControllerEventReceived() "Game controller event" } else -> "Other key event: ${event.source}" } // Do something based on source type findViewById(R.id.text_message).text = outputMessage } // Pass event up to system return super.onKeyDown(keyCode, event) }
// Trigger game events based on key release // Both keyboard and game controller events come through as key events override fun onKeyUp(keyCode: Int, event: KeyEvent?): Boolean { when(keyCode) { KeyEvent.KEYCODE_N -> { MyStateMachine.keyboardEventReceived() engageNitro() return true // event handled here, return true } } // If event not handled, pass up to system return super.onKeyUp(keyCode, event) }
Remarque : Pour les événements clavier, vous pouvez choisir d'utiliser onKeyDown() ou onKeyUp(). Ici, onKeyDown() est utilisé pour contrôler la machine à états, tandis que onKeyUp() est utilisé pour déclencher des événements de jeu.
Si un utilisateur appuie de manière prolongée sur un bouton, onKeyUp() ne sera déclenché qu'une seule fois par pression sur une touche, tandis que onKeyDown() sera appelé plusieurs fois. Si vous souhaitez réagir à la pression vers le bas, vous devez gérer les événements de jeu dans onKeyDown() et implémenter une logique pour traiter les événements répétés. Pour en savoir plus, consultez la documentation sur la gestion des actions du clavier.
Écran tactile et stylet
// Touch and stylus events come through as touch events override fun onTouchEvent(event: MotionEvent?): Boolean { if (event != null) { // Get tool type val pointerIndex = event.action and ACTION_POINTER_INDEX_MASK shr ACTION_POINTER_INDEX_SHIFT val toolType = event.getToolType(pointerIndex) // Check tool type val outputMessage = when (toolType) { TOOL_TYPE_FINGER -> { MyStateMachine.TouchEventReceived() "Touch event" } TOOL_TYPE_STYLUS -> { MyStateMachine.StylusEventReceived() "Stylus event" } else -> "Other touch event: ${toolType}" } // Do something based on tool type, return true if event handled findViewById(R.id.text_message).text = outputMessage } // If event not handled, pass up to system return super.onGenericMotionEvent(event) }
Souris et joystick
// Mouse and joystick events come through as generic events override fun onGenericMotionEvent(event: MotionEvent?): Boolean { if (event != null) { // Check input source val outputMessage = when (event.source) { SOURCE_JOYSTICK -> { MyStateMachine.ControllerEventReceived() "Controller event" } SOURCE_MOUSE -> { MyStateMachine.MouseEventReceived() "Mouse event" } else -> "Other generic event: ${event.source}" } // Do something based on source type, return true if event handled findViewById(R.id.text_message).text = outputMessage } // If event not handled, pass up to system return super.onGenericMotionEvent(event) }