Apps entwickeln

Die folgenden Informationen richten sich an App-Entwickler.

Damit Ihre App Drehknöpfe unterstützt, MÜSSEN Sie Folgendes tun:

  1. Platzieren Sie ein FocusParkingView im jeweiligen Aktivitätslayout.
  2. Achten Sie darauf, dass die Ansichten fokussierbar sind oder nicht.
  3. Verwenden Sie FocusArea, um alle Ansichten, die sich aufrufen lassen, mit Ausnahme der FocusParkingView, einzurücken.

Jede dieser Aufgaben wird unten beschrieben, nachdem Sie Ihre Umgebung für die Entwicklung von Apps mit Drehknopf eingerichtet haben.

Drehregler einrichten

Bevor Sie mit der Entwicklung von Apps mit Drehknopf beginnen können, benötigen Sie entweder einen Drehknopf oder ein Ersatzgerät. Sie haben folgende Möglichkeiten:

Emulator

source build/envsetup.sh && lunch car_x86_64-userdebug
m -j
emulator -wipe-data -no-snapshot -writable-system

Sie können aber auch aosp_car_x86_64-userdebug verwenden.

So greifen Sie auf den emulierten Drehregler zu:

  1. Tippen Sie unten in der Symbolleiste auf das Dreipunkt-Menü:

    Auf emulierten Drehregler zugreifen
    Abbildung 1 Auf emulierten Drehregler zugreifen
  2. Wählen Sie im Fenster „Erweiterte Einstellungen“ die Option Autodrehknopf aus:

    „Auto-Drehscheibe“ auswählen
    Abbildung 2 Wählen Sie „Autodrehkreuz“ aus.

USB-Tastatur

  • Schließen Sie eine USB-Tastatur an Ihr Gerät an, auf dem Android Automotive OS (AAOS) ausgeführt wird. In einigen Fällen wird dadurch verhindert, dass die Bildschirmtastatur angezeigt wird.
  • Verwenden Sie eine userdebug- oder eng-Version.
  • Filterung nach Schlüsselereignissen aktivieren:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • In der folgenden Tabelle finden Sie den entsprechenden Schlüssel für jede Aktion:
    Schlüssel Drehknopf
    F Gegen den Uhrzeigersinn drehen
    E Im Uhrzeigersinn drehen
    A Präzisionsausrichtung links
    D Präzisionsausrichtung rechts
    W Präzisionsausrichtung oben
    S Präzisionsausrichtung unten
    F oder Komma Mitteltaste
    R oder Esc Schaltfläche „Zurück“

ADB-Befehle

Mit car_service-Befehlen können Sie Drehrad-Eingabeereignisse einschleusen. Diese Befehle können auf Geräten mit Android Automotive OS (AAOS) oder auf einem Emulator ausgeführt werden.

car_service-Befehle Drehknopf
adb shell cmd car_service inject-rotary Gegen den Uhrzeigersinn drehen
adb shell cmd car_service inject-rotary -c true Im Uhrzeigersinn drehen
adb shell cmd car_service inject-rotary -dt 100 50 Mehrmals gegen den Uhrzeigersinn drehen (vor 100 ms und vor 50 ms)
adb shell cmd car_service inject-key 282 Präzisionsausrichtung links
adb shell cmd car_service inject-key 283 Präzisionsausrichtung rechts
adb shell cmd car_service inject-key 280 Präzisionsausrichtung oben
adb shell cmd car_service inject-key 281 Präzisionsausrichtung unten
adb shell cmd car_service inject-key 23 Klick auf die mittlere Schaltfläche
adb shell input keyevent inject-key 4 Klick auf die Schaltfläche „Zurück“

Drehregler des OEMs

Wenn die Hardware des Drehknopfs einsatzbereit ist, ist dies die realistischste Option. Sie eignet sich besonders für Tests mit schneller Rotation.

FocusParkingView

FocusParkingView ist eine transparente Ansicht in der Car UI Library (car-ui-library). RotaryService verwendet es, um die Navigation mit dem Drehregler zu unterstützen. FocusParkingView muss die erste Ansicht im Layout sein, die fokussiert werden kann. Sie muss außerhalb aller FocusAreas platziert werden. Jedes Fenster muss einen FocusParkingView haben. Wenn Sie bereits das Basislayout der car-ui-library verwenden, das eine FocusParkingView enthält, müssen Sie keine weitere FocusParkingView hinzufügen. Unten sehen Sie ein Beispiel für FocusParkingView in 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>

Hier sind die Gründe, warum Sie eine FocusParkingView benötigen:

  1. Unter Android wird der Fokus nicht automatisch aufgehoben, wenn er auf ein anderes Fenster gesetzt wird. Wenn Sie versuchen, den Fokus im vorherigen Fenster aufzuheben, stellt Android die Ansicht in diesem Fenster wieder scharf, sodass der Fokus auf zwei Fenster gleichzeitig gelegt wird. Dieses Problem lässt sich möglicherweise dadurch beheben, dass jedem Fenster ein FocusParkingView hinzugefügt wird. Diese Ansicht ist transparent und die standardmäßige Fokus-Hervorhebung ist deaktiviert. Sie ist für den Nutzer also nicht sichtbar, unabhängig davon, ob der Fokus darauf liegt oder nicht. Es kann den Fokus übernehmen, damit RotaryService den Fokus darauf parken kann, um das Fokus-Highlight zu entfernen.
  2. Wenn sich im aktuellen Fenster nur eine FocusArea befindet, bewegt RotaryService den Fokus durch Drehen des Steuerelements in der FocusArea von der Ansicht auf der rechten Seite zur Ansicht auf der linken Seite (und umgekehrt). Wenn Sie diese Ansicht jedem Fenster hinzufügen, kann das Problem behoben werden. Wenn RotaryService feststellt, dass das Fokusziel ein FocusParkingView ist, kann es erkennen, dass ein Umlauf kurz bevorsteht. In diesem Fall wird der Umlauf vermieden, indem der Fokus nicht verschoben wird.
  3. Wenn eine App über den Drehregler gestartet wird, legt Android den Fokus auf die erste fokussierbare Ansicht fest, also immer auf die FocusParkingView. Der FocusParkingView bestimmt die optimale Ansicht, auf die der Fokus gelegt werden soll, und wendet dann den Fokus an.

Fokussierbare Ansichten

RotaryService baut auf dem vorhandenen Konzept des Ansichtsfokus des Android-Frameworks auf, das aus der Zeit stammt, als Smartphones noch physische Tastaturen und D-Pads hatten. Das vorhandene android:nextFocusForward-Attribut wird für Drehknöpfe verwendet (siehe Anpassung von Fokusbereichen), android:nextFocusLeft, android:nextFocusRight, android:nextFocusUp und android:nextFocusDown jedoch nicht.

RotaryService konzentriert sich nur auf Ansichten, die scharf gestellt werden können. Einige Ansichten, z. B. Button, können normalerweise fokussiert werden. Andere, z. B. TextView und ViewGroup, sind es in der Regel nicht. Klickbare Ansichten können automatisch fokussiert werden und Ansichten sind automatisch anklickbar, wenn sie einen Klick-Listener haben. Wenn diese automatische Logik die gewünschte Fokussierbarkeit ergibt, müssen Sie die Fokussierbarkeit der Ansicht nicht explizit festlegen. Wenn die automatische Logik nicht zum gewünschten Fokus führt, legen Sie das Attribut android:focusable auf true oder false fest oder legen Sie die Fokusierbarkeit der Ansicht programmatisch mit View.setFocusable(boolean) fest. Damit RotaryService den Fokus darauf legt, muss eine Ansicht die folgenden Anforderungen erfüllen:

  • Fokussierbar
  • Aktiviert
  • Sichtbar
  • Die Werte für „Breite“ und „Höhe“ müssen ungleich 0 sein.

Wenn eine Ansicht nicht alle diese Anforderungen erfüllt, z. B. eine Schaltfläche, die fokussiert werden kann, aber deaktiviert ist, kann der Nutzer sie nicht mit dem Drehknopf fokussieren. Wenn Sie den Fokus auf deaktivierte Ansichten legen möchten, sollten Sie statt android:state_enabled einen benutzerdefinierten Status verwenden, um festzulegen, wie die Ansicht angezeigt wird, ohne anzugeben, dass Android sie als deaktiviert betrachten soll. Ihre App kann den Nutzer darüber informieren, warum die Ansicht deaktiviert ist, wenn er darauf tippt. Im nächsten Abschnitt wird dies erläutert.

Benutzerdefinierter Status

So fügen Sie einen benutzerdefinierten Status hinzu:

  1. Sie können Ihrer Ansicht ein benutzerdefiniertes Attribut hinzufügen. Wenn Sie beispielsweise der Ansichtsklasse CustomView einen benutzerdefinierten Status state_rotary_enabled hinzufügen möchten, verwenden Sie Folgendes:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Um diesen Status zu erfassen, fügen Sie Ihrer Ansicht eine Instanzvariable zusammen mit Zugriffsmethoden hinzu:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. So rufen Sie den Wert Ihres Attributs beim Erstellen der Datenansicht ab:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. Überschreiben Sie in Ihrer Ansichtsklasse die Methode onCreateDrawableState() und fügen Sie gegebenenfalls den benutzerdefinierten Status hinzu. Beispiel:
    @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. Sie können festlegen, dass der Klick-Handler der Ansicht je nach Status unterschiedlich ausgeführt wird. Der Klick-Handler kann beispielsweise nichts tun oder ein Pop-up anzeigen, wenn mRotaryEnabled = false ist.
  6. Wenn die Schaltfläche deaktiviert erscheinen soll, verwenden Sie im Hintergrund-Zeichnenobjekt Ihrer Ansicht app:state_rotary_enabled anstelle von android:state_enabled. Falls noch nicht geschehen, müssen Sie Folgendes hinzufügen:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Wenn Ihre Ansicht in einigen Layouts deaktiviert ist, ersetzen Sie android:enabled="false" durch app:state_rotary_enabled="false" und fügen Sie dann wie oben beschrieben den Namespace app hinzu.
  8. Wenn Ihre Ansicht programmatisch deaktiviert ist, ersetzen Sie Aufrufe von setEnabled() durch Aufrufe von setRotaryEnabled().

FocusArea

Verwenden Sie FocusAreas, um die fokussierbaren Ansichten in Blöcke zu unterteilen, um die Navigation zu erleichtern und für Konsistenz mit anderen Apps zu sorgen. Wenn Ihre App beispielsweise eine Symbolleiste hat, sollte diese in einer separaten FocusArea vom Rest der App getrennt sein. Tab-Leisten und andere Navigationselemente sollten ebenfalls vom Rest der App getrennt sein. Große Listen sollten in der Regel eine eigene FocusArea haben. Andernfalls müssen Nutzer die gesamte Liste durchgehen, um auf einige Ansichten zuzugreifen.

FocusArea ist eine Unterklasse von LinearLayout in der car-ui-library. Wenn diese Funktion aktiviert ist, wird in FocusArea ein Highlight angezeigt, wenn einer ihrer Nachkommen den Fokus hat. Weitere Informationen finden Sie unter Fokuspunkteigenschaften anpassen.

Wenn Sie beim Erstellen eines Navigationsblocks in der Layoutdatei LinearLayout als Container für diesen Block verwenden möchten, verwenden Sie stattdessen FocusArea. Andernfalls setzen Sie den Block in FocusArea.

FocusArea darf NICHT in einem anderen FocusArea verschachtelt sein. Dies führt zu nicht definiertem Navigationsverhalten. Achten Sie darauf, dass alle Ansichten, die fokussiert werden können, in einem FocusArea verschachtelt sind.

Hier ein Beispiel für eine FocusArea in RotaryPlayground:

<com.android.car.ui.FocusArea
       android:layout_margin="16dp"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:orientation="vertical">
       <EditText
           android:layout_width="match_parent"
           android:layout_height="wrap_content"
           android:singleLine="true">
       </EditText>
   </com.android.car.ui.FocusArea>

FocusArea funktioniert so:

  1. Bei der Verarbeitung von Dreh- und Verschiebeaktionen sucht RotaryService in der Ansichtshierarchie nach Instanzen von FocusArea.
  2. Wenn ein Drehereignis empfangen wird, verschiebt RotaryService den Fokus auf eine andere Ansicht, die den Fokus in derselben FocusArea übernehmen kann.
  3. Wenn ein Nudge-Ereignis empfangen wird, RotaryServiceverschiebt der Fokus auf eine andere Ansicht, die den Fokus auf eine andere (in der Regel benachbarte) FocusArea lenken kann.

Wenn Sie in Ihrem Layout keine FocusAreas einfügen, wird die Stammansicht als impliziter Fokusbereich behandelt. Der Nutzer kann nicht durch Tippen durch die App navigieren. Stattdessen werden alle Ansichten, die fokussiert werden können, nacheinander angezeigt. Das ist für Dialoge möglicherweise ausreichend.

Fokusbereich anpassen

Mit zwei Standardeigenschaften für Ansichten können Sie die Drehknopfnavigation anpassen:

  • Mit android:nextFocusForward können App-Entwickler die Reihenfolge der Drehung in einem Fokusbereich angeben. Mit diesem Attribut wird auch die Tabulatorreihenfolge für die Tastaturnavigation gesteuert. Verwenden Sie dieses Attribut NICHT, um eine Schleife zu erstellen. Verwenden Sie stattdessen app:wrapAround (siehe unten), um eine Schleife zu erstellen.
  • Mit android:focusedByDefault können App-Entwickler die Standardansicht für den Fokus im Fenster angeben. Verwenden Sie dieses Attribut und app:defaultFocus (siehe unten) NICHT im selben FocusArea.

FocusArea definiert auch einige Attribute, um die Drehknopfnavigation anzupassen. Implizite Fokusbereiche können nicht mit diesen Attributen angepasst werden.

  1. (Android 11 QPR3, Android 11 Auto, Android 12)
    app:defaultFocus kann verwendet werden, um die ID einer fokussierbaren untergeordneten Ansicht anzugeben, die fokussiert werden soll, wenn der Nutzer zu dieser FocusArea wischt.
  2. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocusOverridesHistory kann auf true festgelegt werden, damit die oben angegebene Ansicht den Fokus erhält, auch wenn der Verlauf angibt, dass eine andere Ansicht in diesem FocusArea den Fokus hatte.
  3. (Android 12)
    Mit app:nudgeLeftShortcut, app:nudgeRightShortcut, app:nudgeUpShortcut und app:nudgeDownShortcut können Sie die ID einer fokussierbaren untergeordneten Ansicht angeben, die fokussiert werden soll, wenn der Nutzer in eine bestimmte Richtung wischt. Weitere Informationen finden Sie unten im Abschnitt Tastenkürzel für Nudges.

    (Android 11 QPR3, Android 11 Car, in Android 12 eingestellt) app:nudgeShortcut und app:nudgeShortcutDirection unterstützten nur eine Tastenkombination für die Funktion „Nudge“.

  4. (Android 11 QPR3, Android 11 Car, Android 12)
    Wenn die Drehung in diesem FocusArea fortgesetzt werden soll, kann app:wrapAround auf true gesetzt werden. Diese Option wird am häufigsten verwendet, wenn Ansichten in einem Kreis oder Oval angeordnet sind.
  5. (Android 11 QPR3, Android 11 Car, Android 12)
    Mit app:highlightPaddingStart, app:highlightPaddingEnd, app:highlightPaddingTop, app:highlightPaddingBottom, app:highlightPaddingHorizontal und app:highlightPaddingVertical können Sie den Abstand des Highlights in diesem FocusArea anpassen.
  6. (Android 11 QPR3, Android 11 Car, Android 12)
    Mit app:startBoundOffset, app:endBoundOffset, app:topBoundOffset, app:bottomBoundOffset, app:horizontalBoundOffset und app:verticalBoundOffset können Sie die wahrgenommenen Grenzen dieses FocusArea anpassen, um ein Ziel für die Aufforderung zu finden.
  7. (Android 11 QPR3, Android 11 Car, Android 12)
    Wenn Sie in den angegebenen Richtungen die ID einer benachbarten FocusArea (oder Zonen) explizit angeben möchten, verwenden Sie app:nudgeLeft, app:nudgeRight, app:nudgeUp und app:nudgeDown. Verwenden Sie diese Option, wenn mit der standardmäßig verwendeten geometrischen Suche kein gewünschtes Ziel gefunden wird.

Mit dem Schieben können Sie in der Regel zwischen den Fokusbereichen wechseln. Bei Tastenkürzeln für Nudges wird manchmal zuerst innerhalb eines FocusArea navigiert, sodass der Nutzer möglicherweise zweimal tippen muss, um zum nächsten FocusArea zu gelangen. Tastenkürzel für Touchbedienung sind nützlich, wenn ein FocusArea eine lange Liste enthält, gefolgt von einer Floating Action Button (Floating Action Button), wie im folgenden Beispiel:

Tastenkürzel für „Nudge“
Abbildung 3 Tastenkürzel für „Nudge“

Ohne die Tastenkürzel für die Nudge-Funktion müsste der Nutzer die gesamte Liste durchblättern, um das FAB zu erreichen.

Anpassung der Fokus-Hervorhebung

Wie bereits erwähnt, basiert RotaryService auf dem vorhandenen Konzept des Android-Frameworks für den Ansichtsfokus. Wenn der Nutzer das Bild dreht und verschiebt, bewegt RotaryService den Fokus, sodass eine Ansicht fokussiert und eine andere defokussiert wird. Wenn auf Android-Geräten der Fokus auf einer Ansicht liegt, gilt Folgendes:

  • Sie haben ein eigenes Fokus-Highlight angegeben. Android zeichnet das Fokus-Highlight der Ansicht.
  • Wenn keine Fokus-Hervorhebung angegeben ist und die Standard-Fokus-Hervorhebung nicht deaktiviert ist, zeichnet Android die Standard-Fokus-Hervorhebung für die Ansicht.

Bei Apps, die für die Bedienung per Berührung entwickelt wurden, werden in der Regel keine geeigneten Fokuspunkte angegeben.

Das standardmäßige Fokus-Highlight wird vom Android-Framework bereitgestellt und kann vom OEM überschrieben werden. App-Entwickler erhalten sie, wenn das von ihnen verwendete Design von Theme.DeviceDefault abgeleitet ist.

Verwenden Sie nach Möglichkeit die standardmäßige Fokus-Hervorhebung, um für eine einheitliche Nutzererfahrung zu sorgen. Wenn Sie einen benutzerdefinierten Fokusbereich (z. B. rund oder oval) benötigen oder ein nicht von Theme.DeviceDefault abgeleitetes Design verwenden, können Sie mit den Ressourcen der car-ui-library für jede Ansicht einen eigenen Fokusbereich festlegen.

Wenn Sie für eine Ansicht ein benutzerdefiniertes Fokus-Highlight angeben möchten, ändern Sie das Hintergrund- oder Vordergrund-Drawable der Ansicht in ein Drawable, das sich unterscheidet, wenn die Ansicht im Fokus ist. Normalerweise ändern Sie den Hintergrund. Wenn das folgende drawable als Hintergrund für eine quadratische Ansicht verwendet wird, wird ein runder Fokus hervorgehoben:

<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 Auto, Android 12) Fett formatierte Ressourcenreferenzen im Beispiel oben geben Ressourcen an, die von der car-ui-library definiert wurden. Der OEM überschreibt diese, damit sie mit der von ihm angegebenen Standard-Akzentuierung übereinstimmen. So ändern sich die Farbe des Fokus-Akzent, die Strichbreite usw. nicht, wenn der Nutzer zwischen einer Ansicht mit einem benutzerdefinierten Fokus-Akzent und einer Ansicht mit dem standardmäßigen Fokus-Akzent wechselt. Das letzte Element ist eine Welle, die für Berührungen verwendet wird. Die Standardwerte für die fett formatierten Ressourcen sehen so aus:

Standardwerte für fett formatierte Ressourcen
Abbildung 4: Standardwerte für fett formatierte Ressourcen

Außerdem ist ein benutzerdefiniertes Fokus-Highlight erforderlich, wenn einer Schaltfläche eine durchgehende Hintergrundfarbe zugewiesen wird, um die Aufmerksamkeit der Nutzer darauf zu lenken, wie im Beispiel unten. Dadurch kann es sein, dass der Fokusbereich nur schwer zu erkennen ist. Geben Sie in diesem Fall ein benutzerdefiniertes Fokus-Highlight mit sekundären Farben an:

Einfarbig
  • (Android 11 QPR3, Android 11 Car, Android 12)
    car_ui_rotary_focus_fill_secondary_color
    car_ui_rotary_focus_stroke_secondary_color
  • (Android 12)
    car_ui_rotary_focus_pressed_fill_secondary_color
    car_ui_rotary_focus_pressed_stroke_secondary_color

Beispiel:

Sie sind fokussiert, aber nicht gedrückt. Konzentriert, gedrückt
Fokussiert, nicht gedrückt Konzentriert, gedrückt

Drehknopf zum Scrollen

Wenn Ihre App RecyclerView verwendet, sollten Sie stattdessen CarUiRecyclerView verwenden. So ist Ihre Benutzeroberfläche einheitlich, da die Anpassung eines OEMs auf alle CarUiRecyclerViews angewendet wird.

Wenn alle Elemente in Ihrer Liste fokussierbar sind, müssen Sie nichts weiter tun. Bei der Drehknopfnavigation wird der Fokus durch die Elemente in der Liste verschoben und die Liste scrollt, um das neu fokussierte Element sichtbar zu machen.

(Android 11 QPR3, Android 11 Auto, Android 12)
Wenn sich Elemente, die fokussiert werden können, und solche, die nicht fokussiert werden können, mischen oder wenn sich alle Elemente nicht fokussieren lassen, können Sie das Drehknopf-Scrollen aktivieren. So kann der Nutzer mit dem Drehknopf nach und nach durch die Liste scrollen, ohne Elemente zu überspringen, die sich nicht fokussieren lassen. Wenn Sie das Drehen zum Scrollen aktivieren möchten, setzen Sie das Attribut app:rotaryScrollEnabled auf true.

(Android 11 QPR3, Android 11 Auto, Android 12)
Du kannst das Drehen zum Scrollen in jeder scrollbaren Ansicht, einschließlich avCarUiRecyclerView, mit der Methode setRotaryScrollEnabled() in CarUiUtils aktivieren. Gehen Sie dazu so vor:

  • Die scrollbare Ansicht so gestalten, dass sie fokussiert werden kann, wenn keine ihrer fokussierbaren untergeordneten Ansichten sichtbar sind,
  • Deaktivieren Sie die standardmäßige Fokus-Hervorhebung in der scrollbaren Ansicht, indem Sie setDefaultFocusHighlightEnabled(false) aufrufen, damit die scrollbare Ansicht nicht den Anschein erweckt, dass der Fokus darauf liegt.
  • Rufen Sie setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) auf, damit die scrollbare Ansicht vor ihren untergeordneten Elementen fokussiert wird.
  • Hören Sie auf MotionEvents mit SOURCE_ROTARY_ENCODER und entweder AXIS_VSCROLL oder AXIS_HSCROLL, um die zu scrollende Entfernung und die Richtung (durch das Schild) anzugeben.

Wenn das Drehen zum Scrollen auf einem CarUiRecyclerView aktiviert ist und der Nutzer den Bildschirm in einen Bereich dreht, in dem keine fokussierbaren Ansichten vorhanden sind, ändert sich die Scrollleiste von grau zu blau, um anzuzeigen, dass die Scrollleiste fokussiert ist. Sie können einen ähnlichen Effekt implementieren, wenn Sie möchten.

Die MotionEvents sind mit denen identisch, die von einem Mausrad generiert werden, mit Ausnahme der Quelle.

Modus für direkte Manipulation

Normalerweise werden mit Wippen und Drehen die verschiedenen Bereiche der Benutzeroberfläche aufgerufen, während durch Drücken der mittleren Schaltfläche eine Aktion ausgeführt wird. Das ist jedoch nicht immer der Fall. Wenn ein Nutzer beispielsweise die Wecklautstärke anpassen möchte, kann er mit dem Drehknopf zum Schieberegler für die Lautstärke gehen, die mittlere Taste drücken, den Drehknopf drehen, um die Wecklautstärke anzupassen, und dann die Schaltfläche „Zurück“ drücken, um zur Navigation zurückzukehren. Dies wird als Direkte Manipulation (DM) bezeichnet. In diesem Modus wird der Drehregler nicht zum Navigieren, sondern zum direkten Interagieren mit der Ansicht verwendet.

Sie haben zwei Möglichkeiten, DM zu implementieren. Wenn Sie nur die Drehung verarbeiten müssen und die Ansicht, die Sie bearbeiten möchten, auf ACTION_SCROLL_FORWARD- und ACTION_SCROLL_BACKWARD-AccessibilityEvents reagiert, verwenden Sie den einfachen Mechanismus. Andernfalls verwenden Sie den erweiterten Mechanismus.

Der einfache Mechanismus ist die einzige Option in Systemfenstern. Apps können beide Mechanismen verwenden.

Einfacher Mechanismus

(Android 11 QPR3, Android 11 Car, Android 12)
Ihre App sollte DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) aufrufen. RotaryService erkennt, wenn sich der Nutzer im DM-Modus befindet, und wechselt in den DM-Modus, wenn der Nutzer die Schaltfläche „Zur Mitte“ drückt, während eine Ansicht fokussiert ist. Im DM-Modus werden Drehungen mit ACTION_SCROLL_FORWARD oder ACTION_SCROLL_BACKWARD ausgeführt und der DM-Modus wird beendet, wenn der Nutzer auf die Schaltfläche „Zurück“ drückt. Der einfache Mechanismus wechselt den ausgewählten Status der Ansicht, wenn der DM-Modus aktiviert oder deaktiviert wird.

Um Nutzern visuell zu signalisieren, dass sie sich im DM-Modus befinden, sollte die Ansicht bei Auswahl anders aussehen. Ändern Sie beispielsweise den Hintergrund, wenn android:state_selected = true ist.

Erweiterter Mechanismus

Die App bestimmt, wann RotaryService in den DM-Modus wechselt und ihn wieder verlässt. Für eine einheitliche Nutzererfahrung sollte durch Drücken der Mitteltaste bei fokussierter Direktnachrichtenansicht der Direktnachrichtenmodus aktiviert und durch Drücken der Schaltfläche „Zurück“ der Direktnachrichtenmodus beendet werden. Wenn die Mitteltaste und/oder die Verschiebung nicht verwendet werden, können sie auch als alternative Möglichkeiten dienen, den DM-Modus zu beenden. In Apps wie Google Maps kann eine Schaltfläche für Direktnachrichten verwendet werden, um den Direktnachrichtenmodus aufzurufen.

Damit der erweiterte DM-Modus unterstützt wird, muss eine Datenansicht:

  1. (Android 11 QPR3, Android 11 Car, Android 12) MUSS auf ein KEYCODE_DPAD_CENTER-Ereignis warten, um in den DM-Modus zu wechseln, und auf ein KEYCODE_BACK-Ereignis, um den DM-Modus zu beenden, und in jedem Fall DirectManipulationHelper.enableDirectManipulationMode() aufrufen. So können Sie auf diese Ereignisse warten:
    • OnKeyListener registrieren
    • oder
    • Maximieren Sie die Ansicht und überschreiben Sie dann die dispatchKeyEvent()-Methode.
  2. SOLLTE auf Nudge-Ereignisse (KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT oder KEYCODE_DPAD_RIGHT) achten, wenn die Ansicht Nudges verarbeiten soll.
  3. MÜSSEN MotionEvents überwachen und die Anzahl der Drehungen in AXIS_SCROLL abrufen, wenn die Ansicht die Drehung verarbeiten soll. Dafür gibt es mehrere Möglichkeiten:
    1. OnGenericMotionListener registrieren
    2. Maximieren Sie die Ansicht und überschreiben Sie die dispatchTouchEvent()-Methode.
  4. Um zu vermeiden, dass Sie im DM-Modus stecken bleiben, müssen Sie den DM-Modus beenden, wenn das Fragment oder die Aktivität, zu der die Ansicht gehört, nicht interaktiv ist.
  5. Es sollte eine visuelle Kennzeichnung geben, die darauf hinweist, dass sich die Ansicht im DM-Modus befindet.

Unten sehen Sie ein Beispiel für eine benutzerdefinierte Ansicht, in der der DM-Modus zum Schwenken und Zoomen einer Karte verwendet wird:

/** 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(); }

Weitere Beispiele finden Sie im Projekt RotaryPlayground.

ActivityView

Bei Verwendung einer Aktivitätsanzeige gilt Folgendes:

  • Der ActivityView sollte nicht fokussierbar sein.
  • (Android 11 QPR3, Android 11 Auto, ��
    ActivityViewFocusParkingViewapp:shouldRestoreFocusfalse
  • Die Inhalte der ActivityView dürfen keine android:focusByDefault Aufrufe haben.

Für den Nutzer sollten Aktivitätsansichten keine Auswirkungen auf die Navigation haben, mit Ausnahme der Tatsache, dass Fokusbereiche keine Aktivitätsansichten umfassen können. Mit anderen Worten: Sie können keinen einzelnen Fokusbereich haben, der Inhalte innerhalb und außerhalb einer ActivityView enthält. Wenn Sie Ihrem ActivityView keine Fokusbereiche hinzufügen, wird der Stamm der Ansichtshierarchie im ActivityView als impliziter Fokusbereich betrachtet.

Tasten, die durch Drücken aktiviert werden

Die meisten Schaltflächen lösen beim Klicken eine Aktion aus. Einige Tasten funktionieren stattdessen, wenn sie gedrückt gehalten werden. Die Tasten „Vor“ und „Zurück“ funktionieren beispielsweise in der Regel, wenn sie gedrückt gehalten werden. Wenn Sie solche Schaltflächen für den Drehknopf unterstützen möchten, gehen Sie so vor:KEYCODE_DPAD_CENTER KeyEvents

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;
});

Dabei führt mRunnable eine Aktion aus (z. B. Zurückspulen) und wird nach einer Verzögerung ausgeführt.

Touch-Modus

Nutzer können mit einem Drehregler auf zwei Arten mit der Headunit in einem Auto interagieren: entweder über den Drehregler oder durch Berühren des Bildschirms. Wenn Sie den Drehregler verwenden, wird eine der Ansichten, die fokussiert werden können, hervorgehoben. Wenn Sie den Bildschirm berühren, wird kein Fokus-Highlight angezeigt. Der Nutzer kann jederzeit zwischen diesen Eingabemodi wechseln:

  • Drehschalter → Touchbedienung Wenn der Nutzer den Bildschirm berührt, verschwindet die Fokusmarkierung.
  • Touch → Drehknopf Wenn der Nutzer die mittlere Taste drückt, dreht oder antippt, wird die Fokusmarkierung angezeigt.

Die Schaltflächen „Zurück“ und „Startseite“ haben keine Auswirkungen auf den Eingabemodus.

Der Drehknopf nutzt das vorhandene Konzept des Touch-Modus von Android. Mit View.isInTouchMode() können Sie ermitteln, welchen Eingabemodus der Nutzer verwendet. Mit OnTouchModeChangeListener kannst du auf Änderungen warten. Sie können die Benutzeroberfläche zwar für den aktuellen Eingabemodus anpassen, aber größere Änderungen sollten Sie vermeiden, da sie verwirrend sein können.

Fehlerbehebung

In einer App, die für Touchbedienung entwickelt wurde, sind verschachtelte Ansichten mit Fokus üblich. Beispiel: Ein FrameLayout umgibt ein ImageButton, auf die beide der Fokus gelegt werden kann. Das ist für Touchbedienung kein Problem, kann aber zu einer schlechten Nutzererfahrung bei Drehknöpfen führen, da der Nutzer den Controller zweimal drehen muss, um zur nächsten interaktiven Ansicht zu gelangen. Für eine gute Nutzererfahrung empfiehlt Google, entweder die äußere Ansicht oder die innere Ansicht fokussierbar zu machen, aber nicht beides.

Wenn eine Taste oder ein Schalter den Fokus verliert, wenn er über den Drehregler gedrückt wird, kann eine der folgenden Bedingungen vorliegen:

  • Die Taste oder der Schalter wird (kurzzeitig oder dauerhaft) deaktiviert, weil die Taste gedrückt wird. In beiden Fällen gibt es zwei Möglichkeiten, das Problem zu beheben:
    • Lassen Sie den Status „android:enabled“ als „true“ und verwenden Sie einen benutzerdefinierten Status, um die Schaltfläche oder den Schalter wie unter Benutzerdefinierter Status beschrieben auszugrauen.
    • Verwenden Sie einen Container, um die Schaltfläche oder den Schalter zu umschließen, und legen Sie fest, dass der Container statt der Schaltfläche oder des Schalters den Fokus erhält. Der Klick-Listener muss sich im Container befinden.
  • Die Taste oder der Schalter wird ersetzt. Beispielsweise kann die Aktion, die ausgeführt wird, wenn die Schaltfläche gedrückt oder der Schalter umgeschaltet wird, ein Aktualisieren der verfügbaren Aktionen auslösen, wodurch vorhandene Schaltflächen durch neue ersetzt werden. Dafür gibt es zwei Möglichkeiten:
    • Anstatt eine neue Schaltfläche oder einen neuen Schalter zu erstellen, können Sie das Symbol und/oder den Text der vorhandenen Schaltfläche oder des vorhandenen Schalters festlegen.
    • Fügen Sie wie oben beschrieben einen Container um die Schaltfläche oder den Schalter herum hinzu, der den Fokus aufnehmen kann.

RotaryPlayground

RotaryPlayground ist eine Referenz-App für Drehknopf. Hier erfahren Sie, wie Sie Drehfunktionen in Ihre Apps einbinden. RotaryPlayground ist in Emulator-Builds und Builds für Geräte mit Android Automotive OS (AAOS) enthalten.

  • RotaryPlayground-Repository: packages/apps/Car/tests/RotaryPlayground/
  • Versionen: Android 11 QPR3, Android 11 Car und Android 12

In der RotaryPlayground App werden links die folgenden Tabs angezeigt:

  • Karten Testen Sie die Navigation zwischen Fokusbereichen und überspringen Sie Elemente, die nicht fokussiert werden können, sowie Texteingaben.
  • Direkte Manipulation Testen Sie Widgets, die den einfachen und erweiterten Modus für die direkte Manipulation unterstützen. Dieser Tab dient speziell zur direkten Bearbeitung im App-Fenster.
  • Manipulation der System-UI Testen Sie Widgets, die die direkte Manipulation unterstützen, in Systemfenstern, in denen nur der einfache Modus für die direkte Manipulation unterstützt wird.
  • Raster Testen Sie die Z‑Muster-Drehnavigation mit Scrollen.
  • Benachrichtigung Testen Sie, ob Sie Vorabbenachrichtigungen durch Tippen auf das Display ein- und ausblenden können.
  • Scrollen Testen Sie das Scrollen durch eine Mischung aus Inhalten, die sich aufrufen lassen, und solchen, die nicht aufrufbar sind.
  • WebView Testen Sie die Navigation über Links in einer WebView.
  • Benutzerdefiniert FocusArea Anpassung von FocusArea testen:
    • Umbruch
    • android:focusedByDefault und app:defaultFocus
    • .
    • Explizite Ziele für Nudges
    • Tastenkürzel für das Anstupsen
    • FocusArea ohne fokussierbare Ansichten.