Le matériel suivant est destiné aux développeurs d’applications.
Pour que votre application prenne en charge la rotation, vous DEVEZ :
- Placez un
FocusParkingView
dans la disposition d'activité respective. - Assurez-vous que les vues sont (ou non) focalisables.
- Utilisez
FocusArea
s pour envelopper toutes vos vues focalisables, à 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 rotatives.
Configurer un contrôleur rotatif
Avant de pouvoir commencer à développer des applications rotatives, vous avez besoin d'un contrôleur rotatif ou d'un remplaçant. Vous disposez des 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 :
- Sélectionnez Car Rotary dans la fenêtre des commandes étendues :
Clavier USB
- Branchez un clavier USB sur votre appareil qui exécute Android Automotive OS (AAOS). Dans certains cas, cela empêche le clavier à l'écran d'apparaître.
- Utilisez une version
userdebug
oueng
. - Activer 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 Tourner dans le sens inverse des aiguilles d'une montre E Le sens des aiguilles d'une montre UN Coup de pouce vers la gauche D Poussez à droite W Poussez vers le haut S Poussez vers le bas F ou virgule Bouton central R ou Échap Bouton Retour
Commandes BAD
Vous pouvez utiliser les commandes car_service
pour injecter des événements d'entrée rotatifs. Ces commandes peuvent être exécutées sur des appareils exécutant Android Automotive OS (AAOS) ou sur un émulateur.
commandes car_service | Entrée rotative |
---|---|
adb shell cmd car_service inject-rotary | Tourner dans le sens inverse des aiguilles d'une montre |
adb shell cmd car_service inject-rotary -c true | Le sens des aiguilles d'une montre |
adb shell cmd car_service inject-rotary -dt 100 50 | Effectuer plusieurs rotations dans le sens inverse des aiguilles d'une montre (il y a 100 ms et 50 ms) |
adb shell cmd car_service inject-key 282 | Coup de pouce vers la gauche |
adb shell cmd car_service inject-key 283 | Poussez à droite |
adb shell cmd car_service inject-key 280 | Poussez vers le haut |
adb shell cmd car_service inject-key 281 | Poussez vers le bas |
adb shell cmd car_service inject-key 23 | Cliquez sur le bouton central |
adb shell input keyevent inject-key 4 | Cliquez 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. C'est particulièrement utile pour tester une rotation rapide.
FocusParkingVoir
FocusParkingView
est une vue transparente dans la bibliothèque Car UI (car-ui-library) . RotaryService
l'utilise pour prendre en charge la navigation du contrôleur rotatif. FocusParkingView
doit être la première vue focalisable dans la mise en page. Il doit être placé en dehors de tous les FocusArea
. Chaque fenêtre doit avoir un FocusParkingView
. Si vous utilisez déjà la disposition de base car-ui-library, qui contient un FocusParkingView
, vous n'avez pas besoin d'en 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 les raisons pour lesquelles vous avez besoin d’un FocusParkingView
:
- Android n'efface pas automatiquement le focus lorsque le focus est défini dans une autre fenêtre. Si vous essayez de supprimer le focus dans la fenêtre précédente, Android recentre une vue dans cette fenêtre, ce qui entraîne le focus simultané sur deux fenêtres. L'ajout d'un
FocusParkingView
à chaque fenêtre peut résoudre ce problème. Cette vue est transparente et sa surbrillance par défaut est désactivée, de sorte qu'elle soit invisible pour l'utilisateur, qu'elle soit focalisée ou non. Il peut prendre le focus afin queRotaryService
puisse garer le focus dessus pour supprimer la surbrillance du focus. - S'il n'y a qu'un seul
FocusArea
dans la fenêtre actuelle, la rotation du contrôleur dansFocusArea
amèneRotaryService
à déplacer le focus de la vue de droite vers la vue de gauche (et vice versa). L'ajout de cette vue à chaque fenêtre peut résoudre le problème. LorsqueRotaryService
détermine que la cible de focus est unFocusParkingView
, il peut déterminer qu'un bouclage est sur le point de se produire, auquel cas il évite le bouclage en ne déplaçant pas le focus. - Lorsque la commande rotative lance une application, Android concentre la première vue focalisable, qui est toujours
FocusParkingView
.FocusParkingView
détermine la vue optimale sur laquelle se concentrer, puis applique la mise au point.
Vues focalisables
RotaryService
s'appuie sur le concept existant de focus d'affichage du framework Android, remontant à l'époque où les téléphones disposaient de claviers physiques et de D-pads. L'attribut android:nextFocusForward
existant est réutilisé pour la rotation (voir personnalisation FocusArea ), mais android:nextFocusLeft
, android:nextFocusRight
, android:nextFocusUp
et android:nextFocusDown
ne le sont pas.
RotaryService
se concentre uniquement sur les vues pouvant être focalisées. Certaines vues, telles que Button
s, peuvent généralement être focalisées. D'autres, tels que TextView
et ViewGroup
, ne le sont généralement pas. Les vues cliquables sont automatiquement focalisables et les vues sont automatiquement cliquables lorsqu'elles disposent d'un écouteur de clics. Si cette logique automatique aboutit à la focalisation souhaitée, vous n'avez pas besoin de définir explicitement la focalisation de la vue. Si la logique automatique n'aboutit pas à la focalisation souhaitée, définissez l'attribut android:focusable
sur true
ou false
, ou définissez par programme la focalisation de la vue avec View.setFocusable(boolean)
. Pour que RotaryService
se concentre dessus, une vue DOIT répondre aux exigences suivantes :
- Focalisable
- 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 focalisable mais désactivé, l'utilisateur ne peut pas utiliser la commande rotative pour se concentrer dessus. 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 la façon dont la vue apparaît sans indiquer qu'Android doit la considérer désactivée. Votre application peut informer l'utilisateur pourquoi la vue est désactivée lorsqu'il est enfoncé. 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 les 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 lors de la création de votre vue :
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. Par 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 fonctionne 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 l'arrière-plan dessinable de votre vue, utilisez
app:state_rotary_enabled
au lieu deandroid:state_enabled
. Si vous ne l'avez pas déjà, vous devrez ajouter :xmlns:app="http://schemas.android.com/apk/res-auto"
- Si votre vue est désactivée dans n'importe quelle mise 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 programme, remplacez les appels à
setEnabled()
par des appels àsetRotaryEnabled()
.
Secteur d'intérêt
Utilisez FocusAreas
pour diviser les vues focalisables en blocs afin de faciliter la navigation et d'être cohérent avec d'autres applications. Par exemple, si votre application dispose d'une barre d'outils, celle-ci doit se trouver dans une FocusArea
distincte du reste de votre application. Les barres d'onglets et 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
. Dans le cas contraire, les utilisateurs doivent parcourir toute la liste 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 ciblé. Pour en savoir plus, consultez Personnalisation des surbrillance du focus .
Lors de la création d'un bloc de navigation dans le fichier de mise en page, si vous avez l'intention d'utiliser un LinearLayout
comme conteneur pour ce bloc, utilisez plutôt FocusArea
. Sinon, enveloppez le bloc dans un FocusArea
.
N'imbriquez PAS un FocusArea
dans un autre FocusArea
. Cela conduit à un comportement de navigation indéfini. Assurez-vous que toutes les vues focalisables sont imbriquées dans un FocusArea
.
Un exemple de FocusArea
dans RotaryPlayground
est présenté ci-dessous :
<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 déplacement,
RotaryService
recherche des instances deFocusArea
dans la hiérarchie des vues. - Lors de la réception d'un événement de rotation,
RotaryService
déplace le focus vers une autre View qui peut prendre le focus dans la mêmeFocusArea
. - Lors de la réception d'un événement de nudge,
RotaryService
déplace le focus vers une autre vue qui peut prendre le focus dans une autreFocusArea
(généralement adjacente).
Si vous n'incluez aucune FocusAreas
dans votre mise en page, la vue racine est traitée comme une zone de focus implicite. L'utilisateur ne peut pas naviguer dans l'application. Au lieu de cela, ils effectueront une rotation dans toutes les vues focalisables, ce qui pourrait convenir aux boîtes de dialogue.
Personnalisation de la zone de focus
Deux attributs de vue standard peuvent être utilisés pour personnaliser la navigation rotative :
-
android:nextFocusForward
permet aux développeurs d'applications de spécifier l'ordre de rotation dans une zone de focus. Il s'agit du même attribut utilisé pour contrôler l'ordre de tabulation 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 rotative. Les zones de focus implicites ne peuvent pas être personnalisées avec ces attributs.
- ( Android 11 QPR3, Android 11 Voiture, Android 12 )
app:defaultFocus
peut être utilisé pour spécifier l'ID d'une vue descendante focalisable, sur laquelle doit se concentrer lorsque l'utilisateur se déplace vers cetteFocusArea
. - ( Android 11 QPR3, Android 11 Voiture, Android 12 )
app:defaultFocusOverridesHistory
peut être défini surtrue
pour que la vue spécifiée ci-dessus prenne le focus même si l'historique indique qu'une autre vue de cetteFocusArea
a été focalisée. - ( Androïde 12 )
Utilisezapp:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
etapp:nudgeDownShortcut
pour spécifier l'ID d'une vue descendante focalisable, sur laquelle se concentrer lorsque l'utilisateur pousse dans une direction donnée. Pour en savoir plus, consultez le contenu des raccourcis Nudge 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 nudge. - ( Android 11 QPR3, Android 11 Voiture, Android 12 )
Pour permettre à la rotation de s'enrouler dans cetteFocusArea
,app:wrapAround
peut être défini surtrue
. Ceci est le plus souvent utilisé lorsque les vues sont disposées en cercle ou en ovale. - ( Android 11 QPR3, Android 11 Voiture, Android 12 )
Pour ajuster le remplissage de la surbrillance dans cetteFocusArea
, utilisezapp:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
etapp:highlightPaddingVertical
. - ( Android 11 QPR3, Android 11 Voiture, Android 12 )
Pour ajuster les limites perçues de cetteFocusArea
afin de trouver une cible de déplacement, utilisezapp:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
,app:horizontalBoundOffset
etapp:verticalBoundOffset
. - ( Android 11 QPR3, Android 11 Voiture, Android 12 )
Pour spécifier explicitement l'ID d'une ou plusieurs zonesFocusArea
adjacentes dans les directions données, utilisezapp:nudgeLeft
,app:nudgeRight
,app:nudgeUp
etapp:nudgeDown
. Utilisez-le lorsque la recherche géométrique utilisée par défaut ne trouve pas la cible souhaitée.
Nudging navigue généralement entre les FocusAreas. Mais avec les raccourcis de déplacement, le déplacement permet parfois de naviguer d'abord dans une FocusArea
de sorte que l'utilisateur devra peut-être effectuer un déplacement deux fois pour accéder au FocusArea
suivant. Les raccourcis Nudge sont utiles lorsqu'un FocusArea
contient une longue liste suivie d'un Floating Action Button , comme dans l'exemple ci-dessous :
Sans le raccourci Nudge, l'utilisateur devrait parcourir toute la liste pour atteindre le FAB.
Personnalisation des points forts de la mise au point
Comme indiqué ci-dessus, RotaryService
s'appuie sur le concept existant de focus de vue du framework Android. Lorsque l'utilisateur effectue une rotation et un coup de coude, RotaryService
déplace la mise au point, focalisant une vue et déconcentrant une autre. Sous Android, lorsqu'une vue est focalisée, si la vue :
- A spécifié sa propre surbrillance du focus, Android dessine la surbrillance du focus de la vue.
- Ne spécifie pas de surbrillance du focus et la surbrillance du focus par défaut n'est pas désactivée, Android dessine la surbrillance du focus par défaut pour la vue.
Les applications conçues pour le toucher ne spécifient généralement pas les points forts appropriés.
La mise en évidence du focus 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 point culminant de forme personnalisée (par exemple, rond 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 car-ui pour spécifier votre propre point culminant pour chaque vue.
Pour spécifier une surbrillance de focus personnalisée pour une vue, modifiez l'arrière-plan ou le premier plan dessinable de la vue en un dessinable qui diffère lorsque la vue est focalisée. En règle générale, vous modifiez l’arrière-plan. Le dessin suivant, s'il est utilisé comme arrière-plan pour une vue carrée, produit un point culminant 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 être cohérent avec la mise en évidence par défaut qu’il spécifie. Cela garantit que la couleur de surbrillance du focus, la largeur du trait, etc. ne changent pas lorsque l'utilisateur navigue entre une vue avec une surbrillance de focus personnalisée et une vue avec la surbrillance de focus 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 apparaissent comme suit :
De plus, une mise en surbrillance personnalisée est nécessaire lorsqu'un bouton reçoit une couleur d'arrière-plan unie pour le porter à l'attention de l'utilisateur, comme dans l'exemple ci-dessous. Cela peut rendre la mise au point difficile à voir. Dans cette situation, spécifiez une surbrillance de mise au point personnalisée à l'aide de couleurs secondaires :
- ( Android 11 QPR3, Android 11 Voiture, Android 12 )
car_ui_rotary_focus_fill_secondary_color
car_ui_rotary_focus_stroke_secondary_color
- ( Androïde 12 )
car_ui_rotary_focus_pressed_fill_secondary_color
car_ui_rotary_focus_pressed_stroke_secondary_color
Par exemple:
Concentré, pas pressé | Concentré, pressé |
Défilement rotatif
Si votre application utilise RecyclerView
s, vous DEVRIEZ plutôt utiliser CarUiRecyclerView
s. Cela garantit que votre interface utilisateur est cohérente avec les autres, car la personnalisation d'un OEM s'applique à tous les CarUiRecyclerView
.
Si les éléments de votre liste peuvent tous être focalisés, vous n'avez rien d'autre à faire. La navigation rotative déplace le focus à travers les éléments de la liste et la liste défile pour rendre visible l'élément nouvellement focalisé.
( Android 11 QPR3, Android 11 Voiture, Android 12 )
S'il existe un mélange d'éléments focalisables et non focalisables, ou si tous les éléments ne peuvent pas être focalisés, vous pouvez activer le défilement rotatif, ce qui permet à l'utilisateur d'utiliser le contrôleur rotatif pour faire défiler progressivement la liste sans sauter les éléments non focalisables. Pour activer le défilement rotatif, définissez l'attribut app:rotaryScrollEnabled
sur true
.
( Android 11 QPR3, Android 11 Voiture, Android 12 )
Vous pouvez activer le défilement rotatif dans n'importe quelle vue déroulante, y compris av CarUiRecyclerView
, avec la méthode setRotaryScrollEnabled()
dans CarUiUtils
. Si vous le faites, vous devez :
- Rendre la vue déroulante focalisable afin qu'elle puisse être ciblée lorsqu'aucune de ses vues descendantes focalisables n'est visible,
- Désactivez la mise en surbrillance par défaut sur la vue défilante en appelant
setDefaultFocusHighlightEnabled(false)
afin que la vue défilante ne semble pas être ciblée, - Assurez-vous que la vue déroulante est ciblée avant ses descendants en appelant
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
. - Écoutez MotionEvents avec
SOURCE_ROTARY_ENCODER
et soitAXIS_VSCROLL
ouAXIS_HSCROLL
pour indiquer la distance à parcourir et la direction (à travers le signe).
Lorsque le défilement rotatif est activé sur un CarUiRecyclerView
et que l'utilisateur pivote vers une zone où aucune vue focalisable n'est présente, la barre de défilement passe du gris au bleu, comme pour indiquer que la barre de défilement est focalisée. Vous pouvez implémenter un effet similaire si vous le souhaitez.
Les MotionEvents sont les mêmes que 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 déplacements et la rotation naviguent dans l'interface utilisateur, tandis que les pressions sur le bouton central agissent, bien que ce ne soit 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 Centre, faire pivoter le contrôleur pour régler le volume de l'alarme, puis appuyer sur le bouton Retour pour revenir à la navigation. . C’est ce qu’on appelle le mode de manipulation directe (DM) . Dans ce mode, le contrôleur rotatif est utilisé pour interagir directement avec la vue plutôt que pour naviguer.
Implémentez DM de deux manières. Si vous avez uniquement besoin de gérer la rotation et que la vue que vous souhaitez manipuler répond de manière appropriée aux ACTION_SCROLL_FORWARD
et ACTION_SCROLL_BACKWARD
AccessibilityEvent
, utilisez le mécanisme simple . Sinon, utilisez le mécanisme avancé .
Le mécanisme simple est la seule option dans les fenêtres système ; les applications peuvent utiliser l’un ou l’autre mécanisme.
Mécanisme simple
( Android 11 QPR3, Android 11 Voiture, Android 12 )
Votre application doit appeler DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
. RotaryService
reconnaît lorsque l'utilisateur est en mode DM et entre en mode DM lorsque l'utilisateur appuie sur le bouton central alors qu'une vue est focalisée. En mode DM, les rotations effectuent ACTION_SCROLL_FORWARD
ou ACTION_SCROLL_BACKWARD
et quittent le mode DM lorsque l'utilisateur appuie sur le bouton Retour. Le mécanisme simple bascule l’état sélectionné de la vue lors de l’entrée et de la sortie du 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
entre et sort du mode DM. Pour une expérience utilisateur cohérente, appuyer sur le bouton Centre avec une vue DM ciblée devrait passer en mode DM et le bouton Retour devrait quitter le mode DM. Si le bouton central et/ou le coup de pouce ne sont pas utilisés, ils peuvent constituer d'autres moyens de quitter le mode DM. Pour les applications telles que Maps, un bouton représentant DM peut être utilisé pour accéder au mode DM.
Pour prendre en charge 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
- DEVRAIT écouter les événements de nudge (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
ouKEYCODE_DPAD_RIGHT
) si la vue doit gérer les nudges. - DEVRAIT écouter
MotionEvent
s et obtenir le nombre de rotations dansAXIS_SCROLL
si la vue souhaite gérer la rotation. Il y a plusieurs moyens de le faire:- Enregistrez un
OnGenericMotionListener
. - Étendez la vue et remplacez sa méthode
dispatchTouchEvent()
.
- Enregistrez un
- Pour éviter d'être bloqué en mode DM, DOIT quitter le mode DM lorsque le fragment ou l'activité auquel appartient la vue n'est pas interactif.
- DEVRAIT fournir un repère visuel pour indiquer que la vue est en mode DM.
Un exemple de vue personnalisée utilisant le mode DM pour effectuer un panoramique et un zoom sur une carte est fourni ci-dessous :
/** 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(); }
D'autres exemples peuvent être trouvés dans le projet RotaryPlayground
.
ActivitéView
Lors de l'utilisation d'un ActivityView :
- L'
ActivityView
ne devrait pas être focalisable. - ( Android 11 QPR3, Android 11 Car, obsolète dans Android 11 )
Le contenu deActivityView
DOIT contenir unFocusParkingView
comme première vue focalisable, et son attributapp:shouldRestoreFocus
DOIT êtrefalse
. - Le contenu de
ActivityView
ne doit pas avoir de vuesandroid:focusByDefault
.
Pour l'utilisateur, les ActivityViews ne devraient 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 focus contenant du contenu à l’intérieur et à l’extérieur d’un ActivityView
. Si vous n'ajoutez aucun FocusAreas à votre ActivityView
, la racine de la hiérarchie des vues dans ActivityView
est considérée comme une zone de focus implicite.
Boutons qui fonctionnent lorsqu'ils sont maintenus enfoncés
La plupart des boutons provoquent une action lorsqu'ils sont cliqués. Certains boutons fonctionnent lorsqu’ils sont maintenus enfoncés. Par exemple, les boutons Avance rapide et Retour rapide fonctionnent généralement lorsqu'ils sont maintenus enfoncés. Pour que ces boutons prennent en charge la rotation, écoutez les 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 lequel mRunnable
effectue une action (telle qu'un rembobinage) et planifie son exécution après un délai.
Mode tactile
Les utilisateurs peuvent utiliser un contrôleur rotatif pour interagir avec l'unité principale d'une voiture de deux manières, soit en utilisant le contrôleur rotatif, soit en touchant l'écran. Lors de l'utilisation du contrôleur rotatif, l'une des vues focalisables est mise en surbrillance. Lorsque vous touchez l’écran, aucune surbrillance de mise au point n’apparaît. L'utilisateur peut basculer entre ces modes de saisie à tout moment :
- Rotatif → toucher. Lorsque l'utilisateur touche l'écran, la surbrillance disparaît.
- Touchez → rotatif. Lorsque l'utilisateur déplace, fait pivoter ou appuie sur le bouton central, la mise au point apparaît.
Les boutons Retour et Accueil n'ont aucun effet sur le mode de saisie.
Le mode rotatif s'appuie sur le concept existant de 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 cela puisse être utilisé pour personnaliser votre interface utilisateur pour le mode de saisie actuel, évitez tout changement majeur car il peut être déconcertant.
Dépannage
Dans une application conçue pour le toucher, il est courant d'avoir des vues focalisables imbriquées. Par exemple, il peut y avoir un FrameLayout
autour d'un ImageButton
, tous deux pouvant être focalisés. Cela ne nuit pas au toucher, mais peut entraîner une mauvaise expérience utilisateur pour la rotation, 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 externe ou la vue interne focalisable, mais pas les deux.
Si un bouton ou un interrupteur perd la mise au point lorsqu'il est enfoncé via le contrôleur rotatif, l'une de ces conditions peut s'appliquer :
- Le bouton ou l'interrupteur est désactivé (brièvement ou indéfiniment) en raison de la pression sur le bouton. Dans les deux cas, il existe deux manières de résoudre ce problème :
- Laissez l'état
android:enabled
surtrue
et utilisez un état personnalisé pour griser le bouton ou le commutateur, comme décrit dans Custom State . - Utilisez un conteneur pour entourer le bouton ou le commutateur et rendre le conteneur focalisable au lieu du bouton ou du commutateur. (L'écouteur de clics doit être sur le conteneur.)
- Laissez l'état
- Le bouton ou l'interrupteur est en cours de remplacement. Par exemple, l'action entreprise lorsque le bouton est enfoncé ou que le commutateur est basculé peut déclencher une actualisation des actions disponibles, provoquant le remplacement de nouveaux boutons par des boutons existants. Il existe deux manières de résoudre ce problème :
- Au lieu de créer un nouveau bouton ou commutateur, définissez l'icône et/ou le texte du bouton ou du commutateur existant.
- Comme ci-dessus, ajoutez un conteneur focalisable autour du bouton ou du commutateur.
Terrain de jeux Rotary
RotaryPlayground
est une application de référence pour le Rotary. Utilisez-le pour apprendre à intégrer des fonctionnalités rotatives dans vos applications. RotaryPlayground
est inclus dans les versions d'émulateur et dans les versions pour les appareils qui exécutent le système d'exploitation Android Automotive (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 :
- Cartes. Testez la navigation dans les zones de mise au point, en ignorant les éléments non focalisables et la saisie de texte.
- Manipulation directe. Testez les widgets prenant en charge 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'interface utilisateur du système. Testez les widgets prenant en charge la manipulation directe dans les fenêtres système où seul le mode de manipulation directe simple est pris en charge.
- Grille. Testez la navigation rotative en z avec défilement.
- Notification. Testez l’entrée et la sortie des notifications tête haute.
- Faire défiler. Testez le défilement à travers un mélange de contenu focalisable et non focalisable.
- Vue Web. Testez la navigation dans les liens dans un
WebView
. -
FocusArea
personnalisée. Testez la personnalisationFocusArea
:- Enrouler autour.
-
android:focusedByDefault
etapp:defaultFocus
. - Cibles de nudge explicites.
- Déplacez les raccourcis.
-
FocusArea
sans vues focalisables.