Le contenu suivant est destiné aux développeurs d'applications.
Pour que votre application soit compatible avec les commandes rotatives, vous devez:
- Placez un
FocusParkingView
dans la mise en page de l'activité correspondante. - Assurez-vous que les vues peuvent (ou ne peuvent pas) être sélectionnées.
- Utilisez des
FocusArea
pour encapsuler toutes vos vues sélectionnables, à l'exception deFocusParkingView
.
Chacune de ces tâches est détaillée ci-dessous, une fois que vous avez configuré votre environnement pour développer des applications compatibles avec les dispositifs rotatifs.
Configurer un contrôleur rotatif
Avant de pouvoir commencer à développer des applications compatibles avec les commandes rotatives, vous avez besoin d'un contrôleur rotatif ou d'un appareil de substitution. Vous avez les options décrites ci-dessous.
Émulateur
source build/envsetup.sh && lunch car_x86_64-userdebug m -j emulator -wipe-data -no-snapshot -writable-system
Vous pouvez également utiliser aosp_car_x86_64-userdebug
.
Pour accéder au contrôleur rotatif émulé:
- Appuyez sur les trois points en bas de la barre d'outils:
Figure 1. Accéder au contrôleur rotatif émulé - Sélectionnez Commande rotative de la voiture dans la fenêtre des commandes avancées:
Figure 2. Sélectionnez "Rotatif pour voiture".
Clavier USB
- Connectez un clavier USB à votre appareil équipé d'Android Automotive OS (AAOS). Dans certains cas, cela empêche l'affichage du clavier à l'écran.
- Utilisez une version
userdebug
oueng
. - Activez le filtrage des événements clés:
adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
- Consultez le tableau ci-dessous pour trouver la clé correspondante à chaque action:
Clé Action rotative Q Faire pivoter dans le sens inverse des aiguilles d'une montre E Faire pivoter dans le sens des aiguilles d'une montre A Déplacer vers la gauche D Déplacer vers la droite W Déplacer vers le haut S Déplacer vers le bas F ou virgule Bouton central R ou Échap Bouton Retour
Commandes ADB
Vous pouvez utiliser des commandes car_service
pour injecter des événements de saisie par dispositif rotatif. Ces commandes peuvent être exécutées sur des appareils exécutant Android Automotive OS (AAOS) ou sur un émulateur.
Commandes car_service | Saisie par dispositif rotatif |
---|---|
adb shell cmd car_service inject-rotary |
Faire pivoter dans le sens inverse des aiguilles d'une montre |
adb shell cmd car_service inject-rotary -c true |
Faire pivoter dans le sens des aiguilles d'une montre |
adb shell cmd car_service inject-rotary -dt 100 50 |
Faire pivoter dans le sens inverse des aiguilles d'une montre plusieurs fois (il y a 100 ms et 50 ms) |
adb shell cmd car_service inject-key 282 |
Déplacer vers la gauche |
adb shell cmd car_service inject-key 283 |
Déplacer vers la droite |
adb shell cmd car_service inject-key 280 |
Déplacer vers le haut |
adb shell cmd car_service inject-key 281 |
Déplacer vers le bas |
adb shell cmd car_service inject-key 23 |
Clic sur le bouton central |
adb shell input keyevent inject-key 4 |
Clic sur le bouton Retour |
Contrôleur rotatif OEM
Lorsque le matériel de votre contrôleur rotatif est opérationnel, il s'agit de l'option la plus réaliste. Il est particulièrement utile pour tester la rotation rapide.
FocusParkingView
FocusParkingView
est une vue transparente dans la bibliothèque d'UI pour voitures (car-ui-library).
RotaryService
l'utilise pour prendre en charge la navigation avec un contrôleur rotatif.
FocusParkingView
doit être la première vue sélectionnable dans la mise en page. Il doit être placé en dehors de tous les FocusArea
. Chaque fenêtre doit comporter un élément FocusParkingView
. Si vous utilisez déjà la mise en page de base de la bibliothèque car-ui, qui contient un FocusParkingView
, vous n'avez pas besoin d'ajouter un autre FocusParkingView
. Vous trouverez ci-dessous un exemple de FocusParkingView
dans RotaryPlayground
.
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <com.android.car.ui.FocusParkingView android:layout_width="wrap_content" android:layout_height="wrap_content"/> <FrameLayout android:layout_width="match_parent" android:layout_height="match_parent"/> </FrameLayout>
Voici pourquoi vous avez besoin d'un FocusParkingView
:
- Android ne supprime pas automatiquement le focus lorsqu'il est défini sur une autre fenêtre. Si vous essayez de supprimer la sélection dans la fenêtre précédente, Android recentre une vue dans cette fenêtre, ce qui entraîne la sélection de deux fenêtres simultanément. Ajouter un
FocusParkingView
à chaque fenêtre peut résoudre ce problème. Cette vue est transparente et son surlignage par défaut est désactivé, de sorte qu'elle est invisible pour l'utilisateur, que le focus soit activé ou non. Il peut prendre le focus afin queRotaryService
puisse le mettre en veille pour supprimer la surbrillance. - S'il n'y a qu'un seul
FocusArea
dans la fenêtre actuelle, la rotation du contrôleur dansFocusArea
entraîne le déplacement du focus de la vue de droite vers la vue de gauche (et inversement) parRotaryService
. Ajouter cette vue à chaque fenêtre peut résoudre le problème. LorsqueRotaryService
détermine que la cible de sélection est unFocusParkingView
, il peut déterminer qu'un retour à la ligne est sur le point de se produire, auquel cas il évite le retour à la ligne en ne déplaçant pas la sélection. - Lorsque le bouton rotatif lance une application, Android met en surbrillance la première vue sélectionnable, qui est toujours
FocusParkingView
.FocusParkingView
détermine la vue optimale à mettre en surbrillance, puis applique la mise au point.
Vues sélectionnables
RotaryService
s'appuie sur le concept existant de mise au point de la vue du framework Android, qui remonte à l'époque où les téléphones étaient dotés de claviers physiques et de pavés directionnels.
L'attribut android:nextFocusForward
existant est réutilisé pour les éléments rotatifs (voir la section Personnalisation de FocusArea), mais android:nextFocusLeft
, android:nextFocusRight
, android:nextFocusUp
et android:nextFocusDown
ne le sont pas.
RotaryService
ne se concentre que sur les vues pouvant être sélectionnées. Certaines vues, telles que les Button
, peuvent généralement être sélectionnées. D'autres, comme les TextView
et les ViewGroup
, ne le sont généralement pas. Les vues cliquables peuvent être sélectionnées automatiquement et sont automatiquement cliquables lorsqu'elles disposent d'un écouteur de clics. Si cette logique automatique permet d'obtenir la sélection souhaitée, vous n'avez pas besoin de définir explicitement la sélection de la vue. Si la logique automatique ne permet pas d'obtenir la sélection souhaitée, définissez l'attribut android:focusable
sur true
ou false
, ou définissez de manière programmatique la sélection de la vue avec View.setFocusable(boolean)
. Pour que RotaryService
puisse s'y concentrer, une vue DOIT répondre aux exigences suivantes:
- Sélectionnable
- Activé
- Visible
- avoir des valeurs non nulles pour la largeur et la hauteur ;
Si une vue ne répond pas à toutes ces exigences (par exemple, un bouton pouvant être sélectionné mais désactivé), l'utilisateur ne peut pas utiliser le dispositif rotatif pour la sélectionner. Si vous souhaitez vous concentrer sur les vues désactivées, envisagez d'utiliser un état personnalisé plutôt que android:state_enabled
pour contrôler l'affichage de la vue sans indiquer qu'Android doit la considérer comme désactivée. Votre application peut informer l'utilisateur de la raison pour laquelle la vue est désactivée lorsqu'il appuie dessus. La section suivante explique comment procéder.
État personnalisé
Pour ajouter un état personnalisé:
- Pour ajouter un attribut personnalisé à votre vue. Par exemple, pour ajouter un état personnalisé
state_rotary_enabled
à la classe de vueCustomView
, utilisez:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- Pour suivre cet état, ajoutez une variable d'instance à votre vue, ainsi que des méthodes d'accès:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- Pour lire la valeur de votre attribut lorsque votre vue est créée:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- Dans votre classe de vue, remplacez la méthode
onCreateDrawableState()
, puis ajoutez l'état personnalisé, le cas échéant. Exemple :@Override protected int[] onCreateDrawableState(int extraSpace) { if (mRotaryEnabled) extraSpace++; int[] drawableState = super.onCreateDrawableState(extraSpace); if (mRotaryEnabled) { mergeDrawableStates(drawableState, { R.attr.state_rotary_enabled }); } return drawableState; }
- Faites en sorte que le gestionnaire de clics de votre vue s'exécute différemment en fonction de son état. Par exemple, le gestionnaire de clics peut ne rien faire ou afficher un toast lorsque
mRotaryEnabled
estfalse
. - Pour que le bouton apparaisse désactivé, dans le drawable d'arrière-plan de votre vue, utilisez
app:state_rotary_enabled
au lieu deandroid:state_enabled
. Si vous ne l'avez pas déjà fait, vous devez ajouter les éléments suivants:xmlns:app="http://schemas.android.com/apk/res-auto"
- Si votre vue est désactivée dans certaines mises en page, remplacez
android:enabled="false"
parapp:state_rotary_enabled="false"
, puis ajoutez l'espace de nomsapp
, comme ci-dessus. - Si votre vue est désactivée par programmation, remplacez les appels à
setEnabled()
par des appels àsetRotaryEnabled()
.
FocusArea
Utilisez FocusAreas
pour partitionner les vues sélectionnables en blocs afin de faciliter la navigation et d'être cohérent avec les autres applications. Par exemple, si votre application comporte une barre d'outils, elle doit se trouver dans un FocusArea
distinct du reste de l'application. Les barres d'onglets et les autres éléments de navigation doivent également être séparés du reste de l'application. Les grandes listes doivent généralement avoir leur propre FocusArea
. Sinon, les utilisateurs doivent faire défiler la liste complète pour accéder à certaines vues.
FocusArea
est une sous-classe de LinearLayout
dans la bibliothèque car-ui.
Lorsque cette fonctionnalité est activée, FocusArea
dessine un surlignage lorsqu'un de ses descendants est sélectionné. Pour en savoir plus, consultez la section Personnaliser les surbrillances de mise au point.
Lorsque vous créez un bloc de navigation dans le fichier de mise en page, si vous prévoyez d'utiliser un LinearLayout
comme conteneur pour ce bloc, utilisez plutôt FocusArea
.
Sinon, encapsulez le bloc dans un FocusArea
.
NE PAS imbriquer un FocusArea
dans un autre FocusArea
.
Cela entraîne un comportement de navigation non défini. Assurez-vous que toutes les vues pouvant être sélectionnées sont imbriquées dans un FocusArea
.
Voici un exemple de FocusArea
dans RotaryPlayground
:
<com.android.car.ui.FocusArea android:layout_margin="16dp" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <EditText android:layout_width="match_parent" android:layout_height="wrap_content" android:singleLine="true"> </EditText> </com.android.car.ui.FocusArea>
FocusArea
fonctionne comme suit:
- Lors de la gestion des actions de rotation et de pincement,
RotaryService
recherche des instances deFocusArea
dans la hiérarchie des vues. - Lorsqu'il reçoit un événement de rotation,
RotaryService
déplace la sélection vers une autre vue pouvant être sélectionnée dans le mêmeFocusArea
. - Lorsqu'il reçoit un événement de rappel,
RotaryService
déplace le focus vers une autre vue pouvant être sélectionnée dans un autreFocusArea
(généralement adjacent).
Si vous n'incluez aucun FocusAreas
dans votre mise en page, la vue racine est traitée comme une zone de sélection implicite. L'utilisateur ne peut pas effectuer de pression pour naviguer dans l'application. Au lieu de cela, il fait défiler toutes les vues pouvant être sélectionnées, ce qui peut être adapté aux boîtes de dialogue.
Personnalisation de FocusArea
Vous pouvez utiliser deux attributs de vue standards pour personnaliser la navigation par dispositif rotatif:
android:nextFocusForward
permet aux développeurs d'applications de spécifier l'ordre de rotation dans une zone de mise au point. Il s'agit du même attribut que celui utilisé pour contrôler l'ordre des tabulations pour la navigation au clavier. N'utilisez PAS cet attribut pour créer une boucle. Utilisez plutôtapp:wrapAround
(voir ci-dessous) pour créer une boucle.android:focusedByDefault
permet aux développeurs d'applications de spécifier la vue de focus par défaut dans la fenêtre. N'utilisez PAS cet attribut etapp:defaultFocus
(voir ci-dessous) dans le mêmeFocusArea
.
FocusArea
définit également certains attributs pour personnaliser la navigation par dispositif rotatif.
Vous ne pouvez pas personnaliser les zones de focus implicites avec ces attributs.
- (Android 11 QPR3, Android 11 Car, Android 12)
app:defaultFocus
peut être utilisé pour spécifier l'ID d'une vue descendante sélectionnable, qui doit être sélectionnée lorsque l'utilisateur effectue un geste de pression sur cetteFocusArea
. - (Android 11 QPR3, Android 11 Car, Android 12)
app:defaultFocusOverridesHistory
peut être défini surtrue
pour que la vue spécifiée ci-dessus soit sélectionnée, même si l'historique indique qu'une autre vue de cetteFocusArea
était sélectionnée. - (Android 12)
Utilisezapp:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
etapp:nudgeDownShortcut
pour spécifier l'ID d'une vue descendante sélectionnable, qui doit être sélectionnée lorsque l'utilisateur effectue un léger mouvement dans une direction donnée. Pour en savoir plus, consultez la section sur les raccourcis de rappel ci-dessous.(Android 11 QPR3, Android 11 Car, obsolète dans Android 12)
app:nudgeShortcut
etapp:nudgeShortcutDirection
ne prenaient en charge qu'un seul raccourci de pression. - (Android 11 QPR3, Android 11 Car, Android 12)
Pour activer la rotation dans ceFocusArea
,app:wrapAround
peut être défini surtrue
. Cette option est généralement utilisée lorsque les vues sont disposées en cercle ou en ovale. - (Android 11 QPR3, Android 11 Car, Android 12)
Pour ajuster la marge intérieure de la mise en surbrillance dans ceFocusArea
, utilisezapp:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
etapp:highlightPaddingVertical
. - (Android 11 QPR3, Android 11 Car, Android 12)
Pour ajuster les limites perçues de cet élémentFocusArea
afin de trouver une cible de nudge, utilisezapp:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
,app:horizontalBoundOffset
etapp:verticalBoundOffset
. - (Android 11 QPR3, Android 11 Car, Android 12)
Pour spécifier explicitement l'ID d'unFocusArea
(ou d'une zone) adjacent dans les itinéraires donnés, utilisezapp:nudgeLeft
,app:nudgeRight
,app:nudgeUp
etapp:nudgeDown
. Utilisez cette option lorsque la recherche géométrique utilisée par défaut ne trouve pas la cible souhaitée.
Les nudges permettent généralement de naviguer entre les zones de focus. Toutefois, avec les raccourcis de rappel, le rappel permet parfois d'abord de naviguer dans un FocusArea
, de sorte que l'utilisateur peut avoir besoin de le faire deux fois pour accéder au FocusArea
suivant. Les raccourcis de nudge sont utiles lorsqu'un FocusArea
contient une longue liste suivie d'un bouton d'action flottant, comme dans l'exemple ci-dessous:

Sans le raccourci de rappel, l'utilisateur devrait faire défiler toute la liste pour atteindre le bouton d'action flottant.
Personnalisation de la mise en surbrillance du focus
Comme indiqué ci-dessus, RotaryService
s'appuie sur le concept existant de ciblage de vue du framework Android. Lorsque l'utilisateur fait pivoter et ajuste l'écran, RotaryService
déplace le focus, en mettant en surbrillance une vue et en désactivant une autre. Dans Android, lorsqu'une vue est sélectionnée, si la vue:
- A spécifié son propre surbrillance de sélection. Android dessine la surbrillance de sélection de la vue.
- Si vous ne spécifiez pas de surbrillance de sélection et que la surbrillance de sélection par défaut n'est pas désactivée, Android dessine la surbrillance de sélection par défaut pour la vue.
Les applications conçues pour l'écran tactile ne spécifient généralement pas les surbrillances de mise au point appropriées.
La mise en surbrillance de l'élément sélectionné par défaut est fournie par le framework Android et peut être remplacée par l'OEM. Les développeurs d'applications le reçoivent lorsque le thème qu'ils utilisent est dérivé de Theme.DeviceDefault
.
Pour une expérience utilisateur cohérente, utilisez autant que possible la mise en surbrillance par défaut.
Si vous avez besoin d'un surlignage de mise au point de forme personnalisée (par exemple, ronde ou en forme de pilule) ou si vous utilisez un thème non dérivé de Theme.DeviceDefault
, utilisez les ressources de la bibliothèque d'UI de voiture pour spécifier votre propre surlignage de mise au point pour chaque vue.
Pour spécifier un surlignage personnalisé pour une vue, remplacez le drawable d'arrière-plan ou de premier plan de la vue par un drawable qui diffère lorsque la vue est sélectionnée. En règle générale, vous devez modifier l'arrière-plan. Si vous utilisez le drawable suivant comme arrière-plan d'une vue carrée, il produit un surlignage de mise au point rond:
<selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:state_focused="true" android:state_pressed="true"> <shape android:shape="oval"> <solid android:color="@color/car_ui_rotary_focus_pressed_fill_color"/> <stroke android:width="@dimen/car_ui_rotary_focus_pressed_stroke_width" android:color="@color/car_ui_rotary_focus_pressed_stroke_color"/> </shape> </item> <item android:state_focused="true"> <shape android:shape="oval"> <solid android:color="@color/car_ui_rotary_focus_fill_color"/> <stroke android:width="@dimen/car_ui_rotary_focus_stroke_width" android:color="@color/car_ui_rotary_focus_stroke_color"/> </shape> </item> <item> <ripple...> ... </ripple> </item> </selector>
(Android 11 QPR3, Android 11 Car, Android 12) Les références de ressources en gras dans l'exemple ci-dessus identifient les ressources définies par la bibliothèque car-ui. L'OEM les remplace pour qu'ils soient cohérents avec la mise en surbrillance de l'élément sélectionné par défaut qu'il spécifie. Cela garantit que la couleur de surbrillance de l'élément sélectionné, la largeur du trait, etc. ne changent pas lorsque l'utilisateur passe d'une vue avec une surbrillance personnalisée à une vue avec la surbrillance par défaut. Le dernier élément est une ondulation utilisée pour le toucher. Les valeurs par défaut utilisées pour les ressources en gras s'affichent comme suit:

De plus, une mise en surbrillance personnalisée est appelée lorsqu'une couleur d'arrière-plan unie est appliquée à un bouton pour attirer l'attention de l'utilisateur, comme dans l'exemple ci-dessous. Cela peut rendre le surlignage de l'élément sélectionné difficile à voir. Dans ce cas, spécifiez un surlignage personnalisé à l'aide de couleurs secondaires:
![]() |
- (Android 11 QPR3, Android 11 Car, Android 12)
car_ui_rotary_focus_fill_secondary_color
car_ui_rotary_focus_stroke_secondary_color
- (Android 12)
car_ui_rotary_focus_pressed_fill_secondary_color
car_ui_rotary_focus_pressed_stroke_secondary_color
Exemple :
![]() |
![]() |
|
En mode focus, mais non enfoncée | En mode ciblé, enfoncé |
Défilement par dispositif rotatif
Si votre application utilise des RecyclerView
, vous DEVEZ utiliser des CarUiRecyclerView
à la place. Cela garantit que votre UI est cohérente avec les autres, car la personnalisation d'un OEM s'applique à tous les CarUiRecyclerView
.
Si tous les éléments de votre liste peuvent être sélectionnés, aucune autre action n'est requise. La navigation par dispositif rotatif déplace le focus sur les éléments de la liste, et la liste défile pour rendre l'élément mis en surbrillance visible.
(Android 11 QPR3, Android 11 Car, Android 12)
Si des éléments peuvent être sélectionnés et d'autres non, ou si tous les éléments ne peuvent pas être sélectionnés, vous pouvez activer le défilement par dispositif rotatif, ce qui permet à l'utilisateur d'utiliser le dispositif rotatif pour faire défiler progressivement la liste sans ignorer les éléments non sélectionnables. Pour activer le défilement rotatif, définissez l'attribut app:rotaryScrollEnabled
sur true
.
(Android 11 QPR3, Android 11 Car, Android 12)
Vous pouvez activer le défilement rotatif dans n'importe quelle vue à faire défiler, y compris avCarUiRecyclerView
, avec la méthode setRotaryScrollEnabled()
dans CarUiUtils
. Dans ce cas, vous devez:
- Rendre la vue déroulante sélectionnable afin qu'elle puisse être sélectionnée lorsqu'aucune de ses vues descendantes sélectionnables n'est visible
- Désactivez la mise en surbrillance de l'élément sélectionné par défaut dans la vue à faire défiler en appelant
setDefaultFocusHighlightEnabled(false)
afin que la vue à faire défiler ne semble pas être sélectionnée. - Assurez-vous que la vue déroulante est mise au point avant ses descendants en appelant
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
. - Écoutez des MotionEvents avec
SOURCE_ROTARY_ENCODER
etAXIS_VSCROLL
ouAXIS_HSCROLL
pour indiquer la distance à faire défiler et la direction (via le signe).
Lorsque le défilement par dispositif rotatif est activé sur un CarUiRecyclerView
et que l'utilisateur fait pivoter l'écran vers une zone où aucune vue ne peut être sélectionnée, la barre de défilement passe du gris au bleu, comme pour indiquer qu'elle est sélectionnée. Vous pouvez implémenter un effet similaire si vous le souhaitez.
Les événements MotionEvents sont identiques à ceux générés par une molette de défilement sur une souris, à l'exception de la source.
Mode de manipulation directe
Normalement, les pressions et la rotation permettent de naviguer dans l'interface utilisateur, tandis que les pressions sur le bouton central permettent d'effectuer une action, mais ce n'est pas toujours le cas. Par exemple, si un utilisateur souhaite régler le volume de l'alarme, il peut utiliser le contrôleur rotatif pour accéder au curseur de volume, appuyer sur le bouton central, faire pivoter le contrôleur pour régler le volume de l'alarme, puis appuyer sur le bouton Retour pour revenir à la navigation. Il s'agit du mode manipulation directe (DM). Dans ce mode, le contrôleur rotatif permet d'interagir directement avec la vue plutôt que de naviguer.
Vous pouvez implémenter les DM de deux manières : Si vous ne devez gérer que la rotation et que la vue que vous souhaitez manipuler répond de manière appropriée aux AccessibilityEvent
ACTION_SCROLL_FORWARD
et ACTION_SCROLL_BACKWARD
, utilisez le mécanisme simple. Sinon, utilisez le mécanisme avancé.
Le mécanisme simple est la seule option disponible dans les fenêtres système. Les applications peuvent utiliser l'un ou l'autre des mécanismes.
Mécanisme simple
(Android 11 QPR3, Android 11 Car, Android 12)
Votre application doit appeler DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
.
RotaryService
reconnaît quand l'utilisateur est en mode DM et passe en mode DM lorsque l'utilisateur appuie sur le bouton Center (Centre) alors qu'une vue est sélectionnée. En mode MP, les rotations effectuent ACTION_SCROLL_FORWARD
ou ACTION_SCROLL_BACKWARD
et quittent le mode MP lorsque l'utilisateur appuie sur le bouton "Retour". Le mécanisme simple active et désactive l'état sélectionné de la vue lorsque vous accédez et quittez le mode DM.
Pour indiquer visuellement que l'utilisateur est en mode DM, faites en sorte que votre vue apparaisse différente lorsqu'elle est sélectionnée. Par exemple, modifiez l'arrière-plan lorsque android:state_selected
est true
.
Mécanisme avancé
L'application détermine quand RotaryService
passe en mode DM et en sort. Pour une expérience utilisateur cohérente, appuyer sur le bouton central avec une vue de DM en mode focus doit activer le mode DM, et le bouton Retour doit quitter le mode DM. Si le bouton central et/ou le nudge ne sont pas utilisés, ils peuvent être utilisés comme alternative pour quitter le mode DM. Pour les applications telles que Maps, un bouton représentant les DM peut être utilisé pour passer en mode DM.
Pour être compatible avec le mode DM avancé, une vue:
- (Android 11 QPR3, Android 11 Car, Android 12) DOIT écouter un événement
KEYCODE_DPAD_CENTER
pour entrer en mode DM et écouter un événementKEYCODE_BACK
pour quitter le mode DM, en appelantDirectManipulationHelper.enableDirectManipulationMode()
dans chaque cas. Pour écouter ces événements, effectuez l'une des opérations suivantes :- Enregistrez un
OnKeyListener
.
ou :
- Étendez la vue, puis remplacez sa méthode
dispatchKeyEvent()
.
- Enregistrez un
- DOIT écouter les événements de rappel (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
ouKEYCODE_DPAD_RIGHT
) si la vue doit gérer les rappels. - DOIT écouter les
MotionEvent
et obtenir le nombre de rotations dansAXIS_SCROLL
si la vue souhaite gérer la rotation. Pour ce faire, vous pouvez procéder de plusieurs manières :- Enregistrez un
OnGenericMotionListener
. - Développez la vue et remplacez sa méthode
dispatchTouchEvent()
.
- Enregistrez un
- Pour éviter de rester bloqué en mode DM, vous devez quitter ce mode lorsque le fragment ou l'activité à laquelle la vue appartient n'est pas interactif.
- DOIT fournir un indice visuel pour indiquer que la vue est en mode DM.
Vous trouverez ci-dessous un exemple de vue personnalisée qui utilise le mode DM pour effectuer un panoramique et un zoom sur une carte:
/** Whether this view is in DM mode. */ private boolean mInDirectManipulationMode;
/** Initializes the view. Called by the constructors. */ private void init() { setOnKeyListener((view, keyCode, keyEvent) -> { boolean isActionUp = keyEvent.getAction() == KeyEvent.ACTION_UP; switch (keyCode) { // Always consume KEYCODE_DPAD_CENTER and KEYCODE_BACK events. case KeyEvent.KEYCODE_DPAD_CENTER: if (!mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = true; DirectManipulationHelper.enableDirectManipulationMode(this, true); setSelected(true); // visually indicate DM mode } return true; case KeyEvent.KEYCODE_BACK: if (mInDirectManipulationMode && isActionUp) { mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); setSelected(false); } return true; // Consume controller nudge events only when in DM mode. // When in DM mode, nudges pan the map. case KeyEvent.KEYCODE_DPAD_UP: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, -10f); return true; case KeyEvent.KEYCODE_DPAD_DOWN: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(0f, 10f); return true; case KeyEvent.KEYCODE_DPAD_LEFT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(-10f, 0f); return true; case KeyEvent.KEYCODE_DPAD_RIGHT: if (!mInDirectManipulationMode) return false; if (isActionUp) pan(10f, 0f); return true; // Don't consume other key events. default: return false; } });
// When in DM mode, rotation zooms the map. setOnGenericMotionListener(((view, motionEvent) -> { if (!mInDirectManipulationMode) return false; float scroll = motionEvent.getAxisValue(MotionEvent.AXIS_SCROLL); zoom(10 * scroll); return true; })); }
@Override public void onPause() { if (mInDirectManipulationMode) { // To ensure that the user doesn't get stuck in DM mode, disable DM mode // when the fragment is not interactive (e.g., a dialog shows up). mInDirectManipulationMode = false; DirectManipulationHelper.enableDirectManipulationMode(this, false); } super.onPause(); }
Vous trouverez d'autres exemples dans le projet RotaryPlayground
.
ActivityView
Lorsque vous utilisez une ActivityView:
ActivityView
ne doit pas pouvoir être sélectionné.- (Android 11 QPR3, Android 11 Car, obsolète dans Android 11)
Le contenu duActivityView
DOIT contenir unFocusParkingView
comme première vue sélectionnable, et son attributapp:shouldRestoreFocus
DOIT êtrefalse
. - Le contenu de
ActivityView
ne doit pas comporter de vuesandroid:focusByDefault
.
Pour l'utilisateur, les ActivityViews ne doivent avoir aucun effet sur la navigation, sauf que les zones de focus ne peuvent pas s'étendre sur les ActivityViews. En d'autres termes, vous ne pouvez pas avoir une seule zone de mise au point contenant du contenu à l'intérieur et en dehors d'un ActivityView
. Si vous n'ajoutez pas de FocusAreas à votre ActivityView
, la racine de la hiérarchie des vues dans la ActivityView
est considérée comme une zone de focus implicite.
Boutons qui fonctionnent lorsqu'ils sont maintenus enfoncés
La plupart des boutons déclenchent une action lorsqu'ils sont cliqués. Certains boutons fonctionnent en revanche lorsque vous les maintenez enfoncés.
Par exemple, les boutons d'avance rapide et de retour en arrière fonctionnent généralement lorsque vous les maintenez enfoncés. Pour que ces boutons soient compatibles avec les boutons rotatifs, écoutez KEYCODE_DPAD_CENTER
KeyEvents
comme suit:
mButton.setOnKeyListener((v, keyCode, event) -> { if (keyCode != KEYCODE_DPAD_CENTER) { return false; } if (event.getAction() == ACTION_DOWN) { mButton.setPressed(true); mHandler.post(mRunnable); } else { mButton.setPressed(false); mHandler.removeCallbacks(mRunnable); } return true; });
Dans ce cas, mRunnable
effectue une action (par exemple, le retour en arrière) et se planifie pour être exécuté après un délai.
Mode tactile
Les utilisateurs peuvent interagir avec l'unité principale d'une voiture de deux manières : à l'aide d'un contrôleur rotatif ou en appuyant sur l'écran. Lorsque vous utilisez le contrôleur rotatif, l'une des vues pouvant être sélectionnées est mise en surbrillance. Lorsque vous appuyez sur l'écran, aucun surlignage de sélection ne s'affiche. L'utilisateur peut passer d'un mode de saisie à l'autre à tout moment:
- Cadran → tactile. Lorsque l'utilisateur appuie sur l'écran, le surlignage de l'élément en cours de sélection disparaît.
- Appuyez sur → rotatif. Lorsque l'utilisateur pousse, fait pivoter ou appuie sur le bouton central, le surlignage de la zone de focus s'affiche.
Les boutons Retour et Accueil n'ont aucun effet sur le mode de saisie.
Le dispositif rotatif s'appuie sur le concept existant d'mode tactile d'Android.
Vous pouvez utiliser View.isInTouchMode()
pour déterminer le mode de saisie utilisé par l'utilisateur. Vous pouvez utiliser OnTouchModeChangeListener
pour écouter les modifications. Bien que vous puissiez utiliser cette fonctionnalité pour personnaliser votre interface utilisateur pour le mode de saisie actuel, évitez toute modification majeure, car elle peut être déconcertante.
Dépannage
Dans une application conçue pour l'écran tactile, il est courant d'avoir des vues enfouies pouvant être sélectionnées.
Par exemple, il peut y avoir un FrameLayout
autour d'un ImageButton
, qui sont tous deux sélectionnables. Cela n'a aucun impact sur l'écran tactile, mais cela peut entraîner une mauvaise expérience utilisateur pour les commandes rotatives, car l'utilisateur doit faire pivoter le contrôleur deux fois pour passer à la vue interactive suivante. Pour une bonne expérience utilisateur, Google vous recommande de rendre la vue extérieure ou la vue intérieure sélectionnable, mais pas les deux.
Si un bouton ou un bouton-poussoir perd la sélection lorsqu'il est enfoncé à l'aide du contrôleur rotatif, l'une des conditions suivantes peut s'appliquer:
- Le bouton ou l'interrupteur est désactivé (brièvement ou indéfiniment) en raison de l'appui sur le bouton. Dans les deux cas, vous pouvez résoudre ce problème de deux manières :
- Laissez l'état
android:enabled
défini surtrue
et utilisez un état personnalisé pour griser le bouton ou le bouton d'activation/de désactivation, comme décrit dans la section État personnalisé. - Entourez le bouton ou le bouton bascule avec un conteneur et rendez-le sélectionnable au lieu du bouton ou du bouton bascule. (L'écouteur de clic doit se trouver sur le conteneur.)
- Laissez l'état
- Le bouton ou le contacteur est en cours de remplacement. Par exemple, l'action effectuée lorsque le bouton est enfoncé ou que le bouton bascule est activé peut déclencher une actualisation des actions disponibles, ce qui entraîne le remplacement des boutons existants par de nouveaux boutons. Pour cela, vous pouvez procéder de deux façons :
- Au lieu de créer un bouton ou un bouton bascule, définissez l'icône et/ou le texte du bouton ou du bouton bascule existant.
- Comme ci-dessus, ajoutez un conteneur sélectionnable autour du bouton ou du bouton bascule.
RotaryPlayground
RotaryPlayground
est une application de référence pour les dispositifs rotatifs. Utilisez-le pour découvrir comment intégrer des fonctionnalités rotatives à vos applications. RotaryPlayground
est inclus dans les builds de l'émulateur et dans les builds pour les appareils exécutant Android Automotive OS (AAOS).
- Dépôt
RotaryPlayground
:packages/apps/Car/tests/RotaryPlayground/
- Versions: Android 11 QPR3, Android 11 Car et Android 12
L'application RotaryPlayground
affiche les onglets suivants sur la gauche:
- Fiches Testez la navigation dans les zones de focus, en ignorant les éléments non sélectionnables et la saisie de texte.
- Manipulation directe Testez les widgets compatibles avec le mode de manipulation directe simple et avancé. Cet onglet est spécifiquement destiné à la manipulation directe dans la fenêtre de l'application.
- Manipulation de l'UI système Testez les widgets compatibles avec la manipulation directe dans les fenêtres système où seul le mode de manipulation directe simple est pris en charge.
- Grille Tester la navigation par dispositif rotatif en forme de Z avec le défilement
- Notification. Testez l'activation et la désactivation des notifications prioritaires.
- Faites défiler l'écran. Testez le défilement d'un mélange de contenus pouvant être sélectionnés et non sélectionnables.
- WebView Testez la navigation via des liens dans un
WebView
. FocusArea
personnalisée Testez la personnalisation deFocusArea
:- Passe à une main sur le côté.
android:focusedByDefault
etapp:defaultFocus
.
- Cibles de rappel explicites.
- Raccourcis de rappel.
FocusArea
sans vue sélectionnable.