Développement d'applications

Le matériel suivant est destiné aux développeurs d'applications.

Pour que votre application prenne en charge la rotation, vous DEVEZ :

  1. Placez un FocusParkingView dans la mise en page de l'activité respective.
  2. Assurez-vous que les vues sont (ou non) focalisables.
  3. Utilisez FocusArea s pour envelopper toutes vos vues focalisables, à l'exception de FocusParkingView .

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 le mode rotatif.

Configurer un contrôleur rotatif

Avant de pouvoir commencer à développer des applications compatibles avec le mode rotatif, vous avez besoin d'un contrôleur rotatif ou d'un remplaçant. 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é :

  1. Appuyez sur les trois points en bas de la barre d'outils :

    Accéder au contrôleur rotatif émulé
    Figure 1. Accéder au contrôleur rotatif émulé
  2. Sélectionnez Car rotary dans la fenêtre des commandes étendues :

    Sélectionnez Rotatif de voiture
    Figure 2. Sélectionnez le bouton rotatif de la voiture

Clavier USB

  • Branchez un clavier USB sur votre appareil qui exécute Android Automotive OS (AAOS). Dans certains cas, cela peut empêcher l'affichage du clavier à l'écran.
  • Utilisez une version userdebug ou eng .
  • 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 touche correspondante à chaque action :
    Clé Action rotative
    Q Tourner dans le sens antihoraire
    E Le sens des aiguilles d'une montre
    UN Pousser à gauche
    D Pousser à droite
    O Pousser vers le haut
    S Pousser vers le bas
    F ou virgule Bouton central
    R ou Échap Bouton retour

Commandes ADB

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 qui exécutent 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 antihoraire
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 Rotation dans le sens antihoraire plusieurs fois (il y a 100 ms et il y a 50 ms)
adb shell cmd car_service inject-key 282 Pousser à gauche
adb shell cmd car_service inject-key 283 Pousser à droite
adb shell cmd car_service inject-key 280 Pousser vers le haut
adb shell cmd car_service inject-key 281 Pousser 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 votre matériel de 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 Car UI Library (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 FocusArea s. 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'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 :

  1. Android n'efface pas automatiquement la mise au point lorsque la mise au point est définie dans une autre fenêtre. Si vous essayez d'effacer la mise au point dans la fenêtre précédente, Android recentre une vue dans cette fenêtre, ce qui entraîne la mise au point sur deux fenêtres simultanément. 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 est invisible pour l'utilisateur, qu'elle soit ciblée ou non. Il peut prendre le focus pour que RotaryService puisse le mettre en surbrillance pour supprimer la surbrillance du focus.
  2. S'il n'y a qu'un seul FocusArea dans la fenêtre actuelle, la rotation du contrôleur dans le FocusArea amène RotaryService à 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. Lorsque RotaryService détermine que la cible de focus est un FocusParkingView , 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.
  3. Lorsque la commande rotative lance une application, Android met au point la première vue focalisable, qui est toujours la FocusParkingView . Le FocusParkingView détermine la vue optimale sur laquelle se concentrer, puis applique le focus.

Vues focalisables

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 avaient des claviers physiques et des D-pads. L'attribut android:nextFocusForward existant est réutilisé pour le rotatif (voir Personnalisation de la zone de mise au point ), mais android:nextFocusLeft , android:nextFocusRight , android:nextFocusUp et android:nextFocusDown ne le sont pas.

RotaryService se concentre uniquement sur les vues focalisables. Certaines vues, telles que Button s, sont généralement focalisables. D'autres, tels que TextView s et ViewGroup s, ne le sont généralement pas. Les vues cliquables sont automatiquement focalisables et les vues sont automatiquement cliquables lorsqu'elles ont un écouteur de clic. 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 ne donne pas la focalisation souhaitée, définissez l'attribut android:focusable sur true ou false , ou définissez par programmation la focalisation de la vue avec View.setFocusable(boolean) . Pour que RotaryService puisse s'y concentrer, 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 le contrôle rotatif 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 l'apparence 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é :

  1. Pour ajouter un attribut personnalisé à votre vue. Par exemple, pour ajouter un état personnalisé state_rotary_enabled à la classe de vue CustomView , utilisez :
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Pour suivre cet état, ajoutez une variable d'instance à votre vue avec les méthodes d'accès :
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. 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);
    
  4. 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;
    }
    
  5. 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 est false .
  6. Pour que le bouton apparaisse désactivé, dans l'arrière-plan de votre vue, utilisez app:state_rotary_enabled au lieu de android:state_enabled . Si vous ne l'avez pas déjà, vous devrez ajouter :
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Si votre vue est désactivée dans toutes les mises en page, remplacez android:enabled="false" par app:state_rotary_enabled="false" , puis ajoutez l'espace de noms app , comme ci-dessus.
  8. Si votre vue est désactivée par programmation, remplacez les appels à setEnabled() par des appels à setRotaryEnabled() .

Secteur d'intérêt

Utilisez FocusAreas pour partitionner les vues focalisables en blocs afin de faciliter la navigation et d'être cohérent avec les autres applications. Par exemple, si votre application dispose d'une barre d'outils, la barre d'outils 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 . Si ce n'est pas le cas, 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, un FocusArea dessine une surbrillance lorsque l'un de ses descendants est mis au point. Pour en savoir plus, voir Personnalisation de la 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 un FocusArea . Sinon, enveloppez le bloc dans un FocusArea .

N'imbriquez PAS une FocusArea dans une autre FocusArea . Cela conduira à 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 illustré 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 :

  1. Lors de la gestion des actions de rotation et de déplacement, RotaryService recherche des instances de FocusArea dans la hiérarchie de la vue.
  2. Lors de la réception d'un événement de rotation, RotaryService déplace le focus vers une autre vue qui peut prendre le focus dans le même FocusArea .
  3. Lors de la réception d'un événement de coup de pouce, RotaryService déplace le focus vers une autre vue qui peut prendre le focus dans un autre FocusArea (généralement adjacent).

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 pousser pour naviguer dans l'application. Au lieu de cela, ils tourneront dans toutes les vues focalisables, ce qui peut être suffisant pour les boîtes de dialogue.

Personnalisation de la zone de mise au point

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ôt app:wrapAround (voir ci-dessous) pour créer une boucle.
  • android:focusedByDefault permet aux développeurs d'applications de spécifier la vue de mise au point par défaut dans la fenêtre. N'utilisez PAS cet attribut et app:defaultFocus (voir ci-dessous) dans le même FocusArea .

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.

  1. ( Android 11 QPR3, Android 11 Voiture, Android 12 )
    app:defaultFocus peut être utilisé pour spécifier l'ID d'une vue descendante focalisable, qui doit être focalisée lorsque l'utilisateur se déplace vers cette FocusArea .
  2. ( Android 11 QPR3, Android 11 Voiture, Android 12 )
    app:defaultFocusOverridesHistory peut être défini sur true pour que la vue spécifiée ci-dessus prenne le focus même si avec l'historique pour indiquer qu'une autre vue dans cette FocusArea a été focalisée.
  3. ( Android 12 )
    Utilisez app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut et app:nudgeDownShortcut pour spécifier l'ID d'une vue descendante focalisable, qui doit être focalisée 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 et app:nudgeShortcutDirection ne prenaient en charge qu'un seul raccourci Nudge.

  4. ( Android 11 QPR3, Android 11 Voiture, Android 12 )
    Pour permettre à la rotation de s'enrouler dans cette FocusArea , app:wrapAround peut être défini sur true . Ceci est généralement utilisé lorsque les vues sont disposées en cercle ou en ovale.
  5. ( Android 11 QPR3, Android 11 Voiture, Android 12 )
    Pour ajuster le rembourrage de la surbrillance dans cette FocusArea , utilisez app:highlightPaddingStart , app:highlightPaddingEnd , app:highlightPaddingTop , app:highlightPaddingBottom , app:highlightPaddingHorizontal et app:highlightPaddingVertical .
  6. ( Android 11 QPR3, Android 11 Voiture, Android 12 )
    Pour ajuster les limites perçues de cette FocusArea afin de trouver une cible de déplacement, utilisez app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , app:bottomBoundOffset , app:horizontalBoundOffset et app:verticalBoundOffset .
  7. ( Android 11 QPR3, Android 11 Voiture, Android 12 )
    Pour spécifier explicitement l'ID d'une ou plusieurs zones FocusArea adjacentes dans les directions données, utilisez app:nudgeLeft , app:nudgeRight , app:nudgeUp et app:nudgeDown . Utilisez ceci lorsque la recherche géométrique utilisée par défaut ne trouve pas la cible souhaitée.

Le nudging navigue généralement entre les FocusAreas. Mais avec les raccourcis de déplacement, le déplacement navigue parfois d'abord dans un FocusArea , de sorte que l'utilisateur peut avoir besoin de déplacer deux fois pour accéder au prochain FocusArea . Les raccourcis Nudge sont utiles lorsqu'un FocusArea contient une longue liste suivie d'un Floating Action Button , comme dans l'exemple ci-dessous :

Raccourci coup de coude
Figure 3. Raccourci Nudge

Sans le raccourci nudge, l'utilisateur devrait parcourir toute la liste pour atteindre le FAB.

Personnalisation de la surbrillance de la mise au point

Comme indiqué ci-dessus, RotaryService s'appuie sur le concept de focus de vue existant du framework Android. Lorsque l'utilisateur tourne et pousse, RotaryService déplace le focus, en focalisant une vue et en défocalisant une autre. Sous Android, lorsqu'une vue est ciblée, si la vue :

  • a spécifié sa propre surbrillance de focus, Android dessine la surbrillance de focus de la vue.
  • ne spécifie pas de surbrillance de focus et que la surbrillance de focus par défaut n'est pas désactivée, Android dessine la surbrillance de focus par défaut pour la vue.

Les applications conçues pour le tactile ne spécifient généralement pas les points forts de mise au point appropriés.

La surbrillance de mise au point 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, comptez autant que possible sur la mise en surbrillance du focus par défaut. Si vous avez besoin d'une surbrillance de mise au point 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 car-ui-library pour spécifier votre propre surbrillance de mise au point pour chaque vue.

Pour spécifier une surbrillance de mise au point personnalisée pour une vue, modifiez le dessin d'arrière-plan ou de premier plan de la vue en un dessin qui diffère lorsque la vue est mise au point. En règle générale, vous changez l'arrière-plan. Le dessin suivant, s'il est utilisé comme arrière-plan pour une vue carrée, produit une surbrillance ronde :

<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 car-ui-library. L'OEM les remplace pour être cohérent avec la surbrillance de mise au point par défaut qu'ils spécifient. 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 :

Valeurs par défaut pour les ressources en gras
Figure 4. Valeurs par défaut pour les ressources en gras

De plus, une surbrillance de mise au point personnalisée est requise 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 surbrillance de la mise au point difficile à voir. Dans cette situation, spécifiez une surbrillance de mise au point personnalisée à l'aide de couleurs secondaires :

Couleur de fond unie
  • ( Android 11 QPR3, Android 11 Voiture, 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

Par exemple:

Concentré, pas presséConcentré, pressé
Concentré, pas pressé Concentré, pressé

Défilement rotatif

Si votre application utilise RecyclerView s, vous DEVEZ utiliser CarUiRecyclerView s à la place. Cela garantit que votre interface utilisateur est cohérente avec les autres, car la personnalisation d'un OEM s'applique à tous les CarUiRecyclerView s.

Si les éléments de votre liste sont tous focalisables, 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 y a un mélange d'éléments focalisables et non focalisables, ou si tous les éléments ne sont pas focalisables, 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 ignorer 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 focalisée lorsqu'aucune de ses vues descendantes focalisables n'est visible,
  • Désactivez la surbrillance par défaut du focus sur la vue défilante en appelant setDefaultFocusHighlightEnabled(false) afin que la vue défilante ne semble pas focalisée,
  • Assurez-vous que la vue déroulante est ciblée avant ses descendants en appelant setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) .
  • Écoutez les MotionEvents avec SOURCE_ROTARY_ENCODER et AXIS_VSCROLL ou AXIS_HSCROLL pour indiquer la distance de défilement 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 manipulation directe

Normalement, les coups de pouce 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 central, 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 l'une des deux manières. Si vous avez seulement besoin de gérer la rotation et que la vue que vous souhaitez manipuler répond correctement 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 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 quand l'utilisateur est en mode DM et passe en mode DM lorsque l'utilisateur appuie sur le bouton central alors qu'une vue est mise au point. 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 Précédent. 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éremment lorsqu'elle est sélectionnée. Par exemple, modifiez l'arrière-plan lorsque android:state_selected vaut true .

Mécanisme avancé

L'application détermine quand RotaryService entre et sort du mode DM. Pour une expérience utilisateur cohérente, appuyez sur le bouton central avec une vue DM ciblée pour entrer en mode DM et le bouton Retour doit quitter le mode DM. Si le bouton central et/ou le coup de pouce ne sont pas utilisés, ils peuvent être des moyens alternatifs de quitter le mode DM. Pour les applications telles que Maps, un bouton pour représenter DM peut être utilisé pour entrer en mode DM.

Pour prendre en charge le mode DM avancé, une vue :

  1. ( 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énement KEYCODE_BACK pour quitter le mode DM, en appelant DirectManipulationHelper.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() .
  2. DEVRAIT écouter les événements de coup de pouce ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT ou KEYCODE_DPAD_RIGHT ) si la vue doit gérer les coups de pouce.
  3. DEVRAIT écouter MotionEvent s et obtenir le nombre de rotations dans AXIS_SCROLL si la vue veut gérer la rotation. Il y a plusieurs moyens de le faire:
    1. Enregistrez un OnGenericMotionListener .
    2. Étendez la vue et remplacez sa méthode dispatchTouchEvent() .
  4. 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.
  5. DEVRAIT fournir un repère visuel pour indiquer que la vue est en mode DM.

Un exemple de vue personnalisée qui utilise 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 .

ActivityView

Lors de l'utilisation d'un ActivityView :

  • L' ActivityView ne doit pas être focalisable.
  • ( Android 11 QPR3, Android 11 Car, obsolète dans Android 11 )
    Le contenu de ActivityView DOIT contenir un FocusParkingView comme première vue focalisable, et son attribut app:shouldRestoreFocus DOIT être false .
  • Le contenu de ActivityView ne doit pas avoir de vues android:focusByDefault .

Pour l'utilisateur, ActivityViews ne devrait avoir aucun effet sur la navigation, sauf que les zones de focus ne peuvent pas s'étendre sur ActivityViews. En d'autres termes, vous ne pouvez pas avoir une seule zone de focus avec du contenu à l'intérieur et à l'extérieur d'un ActivityView . Si vous n'ajoutez pas de 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 Fast Forward et Rewind fonctionnent généralement lorsqu'ils sont maintenus enfoncés. Pour que ces boutons prennent en charge le rotatif, écoutez KeyEvents KEYCODE_DPAD_CENTER 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 prend une action (telle que le rembobinage) et se programme pour s'exécuter après un délai.

Mode tactile

Les utilisateurs peuvent utiliser un contrôleur rotatif pour interagir avec l'unité principale dans 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 sera 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 de la mise au point disparaît.
  • Appuyez sur → rotatif. Lorsque l'utilisateur pousse, tourne ou appuie sur le bouton central, la surbrillance de la mise au point apparaît.

Les boutons Retour et Accueil n'ont aucun effet sur le mode de saisie.

Rotary ferroutage 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 tactile, il n'est pas rare d'avoir des vues focalisables imbriquées. Par exemple, il peut y avoir un FrameLayout autour d'un ImageButton , qui sont tous deux focalisables. Cela ne nuit pas au toucher, mais cela peut entraîner une mauvaise expérience utilisateur pour le rotatif, 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 focalisable, mais pas les deux.

Si un bouton ou un commutateur perd le focus lorsqu'il est pressé 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 l'appui sur le bouton. Dans les deux cas, il y a deux façons d'y remédier :
    • Laissez l'état android:enabled sur true et utilisez un état personnalisé pour griser le bouton ou le commutateur comme décrit dans État personnalisé .
    • Utilisez un conteneur pour entourer le bouton ou l'interrupteur et rendre le conteneur focalisable au lieu du bouton ou de l'interrupteur. (L'écouteur de clic doit se trouver sur le conteneur.)
  • Le bouton ou l'interrupteur est remplacé. Par exemple, l'action entreprise lorsque le bouton est enfoncé ou que le commutateur est basculé peut déclencher une actualisation des actions disponibles, ce qui fait que de nouveaux boutons remplacent les boutons existants. Il y a deux façons d'aborder cela :
    • Au lieu de créer un nouveau bouton ou commutateur, définissez l'icône et/ou le texte du bouton ou commutateur existant.
    • Comme ci-dessus, ajoutez un conteneur focalisable autour du bouton ou de l'interrupteur.

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 Android Automotive OS (AAOS).

  • Référentiel 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 autour des zones de mise au point, en sautant les éléments non focalisables et la saisie de texte.
  • Manipulation directe. Testez les widgets qui prennent 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 système. Testez les widgets qui prennent 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 z-pattern 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.
  • WebView. Testez la navigation à travers les liens dans un WebView .
  • FocusArea personnalisée . Testez la personnalisation FocusArea :
    • Enrouler autour.
    • android:focusedByDefault et app:defaultFocus
    • .
    • Cibles de coup de pouce explicites.
    • Nudge raccourcis.
    • FocusArea sans vues focalisables.