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 disposition d'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 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é :

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

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

    Sélectionnez le rotatif de voiture
    Figure 2. Sélectionnez le rotatif de 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'apparition 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 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 :

  1. 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 que RotaryService puisse garer le focus dessus 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 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 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 pouvant être mis au point 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é :

  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 ainsi que 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 dessinable 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 n'importe quelle mise 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 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, une FocusArea dessinera un surbrillance lorsqu'un de ses descendants est focalisé. 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 un FocusArea . Sinon, enveloppez le bloc dans un FocusArea .

N'imbriquez PAS un FocusArea dans un autre FocusArea . Cela entraînerait 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 :

  1. Lors de la gestion des actions de rotation et de déplacement, RotaryService recherche des instances de FocusArea dans la hiérarchie des vues.
  2. 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ême FocusArea .
  3. 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 autre FocusArea (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 pivoteront dans toutes les vues focalisables, ce qui peut 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ô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 focus 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, sur laquelle doit se concentrer 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 l'historique indique qu'une autre vue de cette FocusArea a été focalisée.
  3. ( Androïde 12 )
    Utilisez app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut et app: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 et app:nudgeShortcutDirection ne prenaient en charge qu'un seul raccourci de 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 le plus souvent utilisé lorsque les vues sont disposées en cercle ou en ovale.
  5. ( Android 11 QPR3, Android 11 Voiture, Android 12 )
    Pour ajuster le remplissage 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-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 nudge, le nudge navigue parfois d'abord dans une FocusArea , de sorte que l'utilisateur peut avoir besoin de pousser deux fois pour accéder à la FocusArea suivante. 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 de déplacement
Figure 3. Raccourci Déplacement

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 que 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 :

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

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 :

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
  • ( 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é
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 soit AXIS_VSCROLL ou AXIS_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 :

  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 nudge ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT ou KEYCODE_DPAD_RIGHT ) si la vue doit gérer les nudges.
  3. DEVRAIT écouter MotionEvent s et obtenir le nombre de rotations dans AXIS_SCROLL si la vue souhaite 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 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 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, 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 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 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 n'est pas rare 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 sur true 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.)
  • 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 les 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é à une manipulation directe dans la fenêtre de l'application.
  • Manipulation de l’interface utilisateur 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 personnalisation FocusArea :
    • Enrouler autour.
    • android:focusedByDefault et app:defaultFocus
    • .
    • Cibles de nudge explicites.
    • Déplacez les raccourcis.
    • FocusArea sans vues focalisables.