Anwendungen entwickeln

Das folgende Material richtet sich an App-Entwickler.

Damit Ihre App Rotation unterstützt, MÜSSEN Sie:

  1. Platzieren Sie ein FocusParkingView im jeweiligen Aktivitätslayout.
  2. Stellen Sie sicher, dass die Ansichten fokussierbar sind (oder nicht).
  3. Verwenden Sie FocusArea s, um alle Ihre fokussierbaren Ansichten mit Ausnahme von FocusParkingView zu umschließen.

Jede dieser Aufgaben wird im Folgenden detailliert beschrieben, nachdem Sie Ihre Umgebung für die Entwicklung rotierender fähiger Apps eingerichtet haben.

Richten Sie einen Drehregler ein

Bevor Sie mit der Entwicklung von Apps mit Drehfunktion beginnen können, benötigen Sie entweder einen Drehregler oder einen Stellvertreter. Sie haben die unten beschriebenen Möglichkeiten.

Emulator

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

Sie können auch aosp_car_x86_64-userdebug verwenden.

So greifen Sie auf den emulierten Drehregler zu:

  1. Tippen Sie auf die drei Punkte unten in der Symbolleiste:

    Greifen Sie auf den emulierten Drehregler zu
    Abbildung 1. Access-emulierter Drehregler
  2. Wählen Sie im Fenster „Erweiterte Steuerung“ die Option „Autodreh“ aus:

    Wählen Sie Autodreh
    Abbildung 2. Wählen Sie Car Rotary

USB-Tastatur

  • Schließen Sie eine USB-Tastatur an Ihr Gerät an, auf dem Android Automotive OS (AAOS) läuft. In einigen Fällen wird dadurch verhindert, dass die Bildschirmtastatur angezeigt wird.
  • Verwenden Sie einen userdebug oder eng Build.
  • Schlüsselereignisfilterung 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 Rotary-Aktion
    Q Gegen den Uhrzeigersinn drehen
    E Im Uhrzeiger sinn drehen
    A Stupse nach links
    D Nach rechts schieben
    W Anstupsen
    S Nach unten schieben
    F oder Komma Mitteltaste
    R oder Esc Zurück-Button

ADB-Befehle

Sie können car_service Befehle verwenden, um Dreheingabeereignisse einzuspeisen. Diese Befehle können auf Geräten mit Android Automotive OS (AAOS) oder auf einem Emulator ausgeführt werden.

car_service-Befehle Dreheingang
adb shell cmd car_service inject-rotary Gegen den Uhrzeigersinn drehen
adb shell cmd car_service inject-rotary -c true Im Uhrzeiger sinn 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 Stupse nach links
adb shell cmd car_service inject-key 283 Nach rechts schieben
adb shell cmd car_service inject-key 280 Anstupsen
adb shell cmd car_service inject-key 281 Nach unten schieben
adb shell cmd car_service inject-key 23 Klicken Sie auf die mittlere Schaltfläche
adb shell input keyevent inject-key 4 Klicken Sie auf die Schaltfläche „Zurück“.

OEM-Drehregler

Wenn Ihre Drehregler-Hardware betriebsbereit ist, ist dies die realistischste Option. Dies ist besonders nützlich zum Testen einer schnellen Rotation.

FocusParkingView

FocusParkingView ist eine transparente Ansicht in der Car-UI-Bibliothek (car-ui-library) . RotaryService nutzt es zur Unterstützung der Drehregler-Navigation. FocusParkingView muss die erste fokussierbare Ansicht im Layout sein. Es muss außerhalb aller FocusArea s platziert werden. Jedes Fenster muss über eine FocusParkingView verfügen. Wenn Sie bereits das Basislayout der car-ui-library verwenden, das ein FocusParkingView enthält, müssen Sie kein weiteres 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 FocusParkingView benötigen:

  1. Android löscht den Fokus nicht automatisch, wenn der Fokus in einem anderen Fenster festgelegt wird. Wenn Sie versuchen, den Fokus im vorherigen Fenster zu löschen, fokussiert Android eine Ansicht in diesem Fenster neu, was dazu führt, dass zwei Fenster gleichzeitig fokussiert werden. Das Hinzufügen einer FocusParkingView zu jedem Fenster kann dieses Problem beheben. Diese Ansicht ist transparent und ihre standardmäßige Fokushervorhebung ist deaktiviert, sodass sie für den Benutzer unsichtbar ist, unabhängig davon, ob sie fokussiert ist oder nicht. Es kann fokussiert werden, sodass RotaryService den Fokus darauf parken kann, um die Fokushervorhebung zu entfernen.
  2. Wenn im aktuellen Fenster nur ein FocusArea vorhanden ist, führt das Drehen des Controllers im FocusArea dazu, dass RotaryService den Fokus von der Ansicht rechts auf die Ansicht links (und umgekehrt) verschiebt. Durch Hinzufügen dieser Ansicht zu jedem Fenster kann das Problem behoben werden. Wenn RotaryService feststellt, dass es sich bei dem Fokusziel um ein FocusParkingView handelt, kann es feststellen, dass ein Umlauf bevorsteht, und an diesem Punkt den Umlauf vermeiden, indem der Fokus nicht verschoben wird.
  3. Wenn der Drehregler eine App startet, fokussiert Android die erste fokussierbare Ansicht, bei der es sich immer um FocusParkingView handelt. FocusParkingView bestimmt die optimale Ansicht zum Fokussieren und wendet dann den Fokus an.

Fokussierbare Ansichten

RotaryService baut auf dem bestehenden Ansichtsfokuskonzept des Android-Frameworks auf, das auf die Zeit zurückgeht, als Telefone über physische Tastaturen und D-Pads verfügten. Das vorhandene android:nextFocusForward Attribut wird für Rotation umfunktioniert (siehe FocusArea-Anpassung ), android:nextFocusLeft , android:nextFocusRight , android:nextFocusUp und android:nextFocusDown jedoch nicht.

RotaryService konzentriert sich nur auf Ansichten, die fokussierbar sind. Einige Ansichten, wie z. B. Button , sind normalerweise fokussierbar. Bei anderen, wie z. B. TextView und ViewGroup , ist dies normalerweise nicht der Fall. Anklickbare Ansichten sind automatisch fokussierbar und Ansichten sind automatisch anklickbar, wenn sie über einen Klick-Listener verfügen. Wenn diese automatische Logik zur gewünschten Fokussierbarkeit führt, müssen Sie die Fokussierbarkeit der Ansicht nicht explizit festlegen. Wenn die automatische Logik nicht zur gewünschten Fokussierbarkeit führt, legen Sie das Attribut android:focusable auf true oder false fest oder legen Sie die Fokussierbarkeit der Ansicht programmgesteuert mit View.setFocusable(boolean) fest. Damit sich RotaryService darauf konzentrieren kann, MUSS eine Ansicht die folgenden Anforderungen erfüllen:

  • Fokussierbar
  • Ermöglicht
  • Sichtbar
  • Werte ungleich Null für Breite und Höhe haben

Wenn eine Ansicht nicht alle diese Anforderungen erfüllt, beispielsweise eine fokussierbare, aber deaktivierte Schaltfläche, kann der Benutzer den Drehknopf nicht verwenden, um darauf zu fokussieren. Wenn Sie sich auf deaktivierte Ansichten konzentrieren möchten, sollten Sie die Verwendung eines benutzerdefinierten Status anstelle von android:state_enabled in Betracht ziehen, um zu steuern, wie die Ansicht angezeigt wird, ohne anzugeben, dass Android sie als deaktiviert betrachten soll. Ihre App kann den Benutzer beim Antippen darüber informieren, warum die Ansicht deaktiviert ist. Im nächsten Abschnitt wird erklärt, wie das geht.

Benutzerdefinierter Status

So fügen Sie einen benutzerdefinierten Status hinzu:

  1. So fügen Sie Ihrer Ansicht ein benutzerdefiniertes Attribut hinzu. Um beispielsweise einen benutzerdefinierten Status state_rotary_enabled “ zur Ansichtsklasse CustomView hinzuzufügen, verwenden Sie:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Um diesen Status zu verfolgen, 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. Um den Wert Ihres Attributs beim Erstellen Ihrer Ansicht zu lesen:
    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 dann 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. Sorgen Sie dafür, dass der Click-Handler Ihrer Ansicht je nach Status eine unterschiedliche Leistung erbringt. Beispielsweise führt der Click-Handler möglicherweise nichts aus oder es wird ein Popup angezeigt, wenn mRotaryEnabled false hat.
  6. Um die Schaltfläche deaktiviert erscheinen zu lassen, verwenden Sie im zeichnbaren Hintergrund Ihrer Ansicht app:state_rotary_enabled anstelle von android:state_enabled . Wenn Sie es noch nicht haben, müssen Sie Folgendes hinzufügen:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Wenn Ihre Ansicht in irgendeinem Layout deaktiviert ist, ersetzen Sie android:enabled="false" durch app:state_rotary_enabled="false" und fügen Sie dann den app Namespace wie oben hinzu.
  8. Wenn Ihre Ansicht programmgesteuert deaktiviert ist, ersetzen Sie Aufrufe von setEnabled() durch Aufrufe von setRotaryEnabled() .

Fokusbereich

Verwenden Sie FocusAreas , um die fokussierbaren Ansichten in Blöcke zu unterteilen, um die Navigation zu vereinfachen und die Konsistenz mit anderen Apps zu gewährleisten. Wenn Ihre App beispielsweise über eine Symbolleiste verfügt, sollte sich die Symbolleiste in einem vom Rest Ihrer App getrennten FocusArea befinden. Auch Tab-Leisten und andere Navigationselemente sollten vom Rest der App getrennt sein. Große Listen sollten grundsätzlich eine eigene FocusArea haben. Wenn nicht, müssen Benutzer 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, zeichnet FocusArea eine Hervorhebung, wenn einer seiner Nachkommen fokussiert ist. Weitere Informationen finden Sie unter Anpassung der Fokushervorhebung .

Wenn Sie beim Erstellen eines Navigationsblocks in der Layoutdatei beabsichtigen, ein LinearLayout als Container für diesen Block zu verwenden, verwenden Sie stattdessen FocusArea . Andernfalls wickeln Sie den Block in eine FocusArea ein.

Verschachteln Sie eine FocusArea NICHT in einer anderen FocusArea . Dies führt zu undefiniertem Navigationsverhalten. Stellen Sie sicher, dass alle fokussierbaren Ansichten innerhalb einer FocusArea verschachtelt sind.

Ein Beispiel für eine FocusArea in RotaryPlayground ist unten dargestellt:

<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 wie folgt:

  1. Bei der Verarbeitung von Rotations- und Nudge-Aktionen sucht RotaryService in der Ansichtshierarchie nach Instanzen von FocusArea .
  2. Beim Empfang eines Rotationsereignisses verschiebt RotaryService den Fokus auf eine andere Ansicht, die den Fokus im selben FocusArea übernehmen kann.
  3. Beim Empfang eines Nudge-Ereignisses verschiebt RotaryService den Fokus auf eine andere Ansicht, die den Fokus in einem anderen (normalerweise benachbarten) FocusArea übernehmen kann.

Wenn Sie keine FocusAreas in Ihr Layout einschließen, wird die Stammansicht als impliziter Fokusbereich behandelt. Der Benutzer kann nicht durch Anstupsen in der App navigieren. Stattdessen rotieren sie durch alle fokussierbaren Ansichten, was für Dialoge ausreichend sein könnte.

Fokusbereich-Anpassung

Zwei standardmäßige Ansichtsattribute können zum Anpassen der Rotationsnavigation verwendet werden:

  • android:nextFocusForward können App-Entwickler die Rotationsreihenfolge in einem Fokusbereich festlegen. Dies ist das gleiche Attribut, das zur Steuerung der Tab-Reihenfolge für die Tastaturnavigation verwendet wird. Verwenden Sie dieses Attribut NICHT , um eine Schleife zu erstellen. Verwenden Sie stattdessen app:wrapAround (siehe unten), um eine Schleife zu erstellen.
  • android:focusedByDefault können App-Entwickler die Standardfokusansicht im Fenster festlegen. Verwenden Sie dieses Attribut und app:defaultFocus (siehe unten) NICHT im selben FocusArea .

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

  1. ( Android 11 QPR3, Android 11 Auto, Android 12 )
    app:defaultFocus kann verwendet werden, um die ID einer fokussierbaren Nachkommenansicht anzugeben, auf die der Fokus gelegt werden soll, wenn der Benutzer zu dieser FocusArea wechselt.
  2. ( Android 11 QPR3, Android 11 Auto, Android 12 )
    app:defaultFocusOverridesHistory kann auf true gesetzt werden, damit die oben angegebene Ansicht den Fokus erhält, auch wenn mit dem Verlauf angegeben wird, dass auf eine andere Ansicht in diesem FocusArea der Fokus gelegt wurde.
  3. ( Android 12 )
    Verwenden Sie app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut und app:nudgeDownShortcut , um die ID einer fokussierbaren Nachfolgeansicht anzugeben, die fokussiert werden soll, wenn der Benutzer in eine bestimmte Richtung bewegt. Weitere Informationen finden Sie unten im Inhalt der Nudge-Verknüpfungen .

    ( Android 11 QPR3, Android 11 Car, veraltet in Android 12 ) app:nudgeShortcut und app:nudgeShortcutDirection unterstützten nur eine Nudge-Verknüpfung.

  4. ( Android 11 QPR3, Android 11 Auto, Android 12 )
    Damit die Rotation in diesem FocusArea umlaufen kann, kann app:wrapAround auf true gesetzt werden. Dies wird am häufigsten verwendet, wenn Ansichten kreisförmig oder oval angeordnet sind.
  5. ( Android 11 QPR3, Android 11 Auto, Android 12 )
    Um den Abstand der Hervorhebung in diesem FocusArea anzupassen, verwenden Sie app:highlightPaddingStart , app:highlightPaddingEnd , app:highlightPaddingTop , app:highlightPaddingBottom , app:highlightPaddingHorizontal und app:highlightPaddingVertical .
  6. ( Android 11 QPR3, Android 11 Auto, Android 12 )
    Um die wahrgenommenen Grenzen dieser FocusArea anzupassen, um ein Schubziel zu finden, verwenden Sie app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , app:bottomBoundOffset , app:horizontalBoundOffset und app:verticalBoundOffset .
  7. ( Android 11 QPR3, Android 11 Auto, Android 12 )
    Um die ID einer benachbarten FocusArea (oder Bereiche) in den angegebenen Richtungen explizit anzugeben, verwenden Sie app:nudgeLeft , app:nudgeRight , app:nudgeUp und app:nudgeDown . Verwenden Sie diese Option, wenn die standardmäßig verwendete geometrische Suche das gewünschte Ziel nicht findet.

Beim Nudging wird normalerweise zwischen Fokusbereichen navigiert. Bei Nudge-Verknüpfungen wird beim Nudging jedoch manchmal zuerst innerhalb einer FocusArea navigiert, sodass der Benutzer möglicherweise zweimal nudgen muss, um zur nächsten FocusArea zu navigieren. Nudge-Verknüpfungen sind nützlich, wenn eine FocusArea eine lange Liste gefolgt von einer schwebenden Aktionsschaltfläche enthält, wie im folgenden Beispiel:

Nudge-Verknüpfung
Abbildung 3. Nudge-Verknüpfung

Ohne die Nudge-Verknüpfung müsste der Benutzer die gesamte Liste durchlaufen, um zum FAB zu gelangen.

Fokus-Highlight-Anpassung

Wie oben erwähnt, baut RotaryService auf dem bestehenden Ansichtsfokuskonzept des Android-Frameworks auf. Wenn sich der Benutzer dreht und bewegt, verschiebt RotaryService den Fokus, fokussiert eine Ansicht und hebt den Fokus einer anderen auf. Wenn in Android eine Ansicht fokussiert ist und die Ansicht:

  • Hat eine eigene Fokushervorhebung angegeben, zeichnet Android die Fokushervorhebung der Ansicht.
  • Gibt keine Fokushervorhebung an und die Standardfokushervorhebung ist nicht deaktiviert. Android zeichnet die Standardfokushervorhebung für die Ansicht.

Für Touch-Apps konzipierte Apps geben in der Regel nicht die entsprechenden Fokushervorhebungen an.

Die standardmäßige Fokushervorhebung wird vom Android-Framework bereitgestellt und kann vom OEM überschrieben werden. App-Entwickler erhalten es, wenn das von ihnen verwendete Theme von Theme.DeviceDefault abgeleitet ist.

Verlassen Sie sich für ein konsistentes Benutzererlebnis nach Möglichkeit auf die standardmäßige Fokushervorhebung. Wenn Sie eine benutzerdefinierte Fokushervorhebung (z. B. rund oder pillenförmig) benötigen oder ein Design verwenden, das nicht von Theme.DeviceDefault abgeleitet ist, verwenden Sie die Ressourcen der car-ui-library, um Ihre eigene Fokushervorhebung anzugeben jede Ansicht.

Um eine benutzerdefinierte Fokushervorhebung für eine Ansicht festzulegen, ändern Sie das Hintergrund- oder Vordergrund-Zeichnbare der Ansicht in ein Zeichenobjekt, das sich unterscheidet, wenn die Ansicht fokussiert ist. Normalerweise ändern Sie den Hintergrund. Das folgende Zeichenobjekt erzeugt, wenn es als Hintergrund für eine quadratische Ansicht verwendet wird, eine runde Fokushervorhebung:

<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 ) Fettgedruckte Ressourcenreferenzen im obigen Beispiel identifizieren Ressourcen, die von der car-ui-library definiert werden. Der OEM überschreibt diese, um mit der von ihnen angegebenen Standardfokushervorhebung konsistent zu sein. Dadurch wird sichergestellt, dass sich die Farbe der Fokushervorhebung, die Strichstärke usw. nicht ändern, wenn der Benutzer zwischen einer Ansicht mit einer benutzerdefinierten Fokushervorhebung und einer Ansicht mit der Standardfokushervorhebung navigiert. Das letzte Element ist eine Welle, die zur Berührung verwendet wird. Die für die fett gedruckten Ressourcen verwendeten Standardwerte sehen wie folgt aus:

Standardwerte für fettgedruckte Ressourcen
Abbildung 4. Standardwerte für fettgedruckte Ressourcen

Darüber hinaus ist eine benutzerdefinierte Fokushervorhebung erforderlich, wenn einer Schaltfläche eine einfarbige Hintergrundfarbe zugewiesen wird, um die Aufmerksamkeit des Benutzers darauf zu lenken, wie im folgenden Beispiel. Dies kann dazu führen, dass die Fokushervorhebung schwer zu erkennen ist. Geben Sie in dieser Situation eine benutzerdefinierte Fokushervorhebung mithilfe von Sekundärfarben an:

Solide Hintergrundfarbe
  • ( Android 11 QPR3, Android 11 Auto, 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

Zum Beispiel:

Konzentriert, nicht unter DruckKonzentriert, gedrückt
Konzentriert, nicht unter Druck Konzentriert, gedrückt

Rotierendes Scrollen

Wenn Ihre App RecyclerView s verwendet, SOLLTEN Sie stattdessen CarUiRecyclerView s verwenden. Dadurch wird sichergestellt, dass Ihre Benutzeroberfläche mit anderen konsistent ist, da die Anpassung eines OEMs für alle CarUiRecyclerView s gilt.

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

( Android 11 QPR3, Android 11 Auto, Android 12 )
Wenn es eine Mischung aus fokussierbaren und nicht fokussierbaren Elementen gibt oder wenn alle Elemente nicht fokussierbar sind, können Sie das Scrollen mit dem Drehregler aktivieren, sodass der Benutzer mit dem Drehregler schrittweise durch die Liste scrollen kann, ohne nicht fokussierbare Elemente zu überspringen. Um den rotierenden Bildlauf zu aktivieren, setzen Sie das Attribut app:rotaryScrollEnabled auf true .

( Android 11 QPR3, Android 11 Auto, Android 12 )
Sie können das rotierende Scrollen in jeder scrollbaren Ansicht, einschließlich CarUiRecyclerView , mit der Methode setRotaryScrollEnabled() in CarUiUtils aktivieren. Wenn Sie dies tun, müssen Sie Folgendes tun:

  • Machen Sie die scrollbare Ansicht fokussierbar, damit sie fokussiert werden kann, wenn keine ihrer fokussierbaren untergeordneten Ansichten sichtbar ist.
  • Deaktivieren Sie die standardmäßige Fokushervorhebung in der scrollbaren Ansicht, indem setDefaultFocusHighlightEnabled(false) aufrufen, sodass die scrollbare Ansicht nicht fokussiert zu sein scheint.
  • Stellen Sie sicher, dass die scrollbare Ansicht vor ihren Nachkommen fokussiert ist, indem Sie setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) aufrufen.
  • Warten Sie mit SOURCE_ROTARY_ENCODER und entweder AXIS_VSCROLL oder AXIS_HSCROLL auf MotionEvents, um die zu scrollende Entfernung und die Richtung (durch das Zeichen) anzugeben.

Wenn das rotierende Scrollen auf einer CarUiRecyclerView aktiviert ist und der Benutzer in einen Bereich dreht, in dem keine fokussierbaren Ansichten vorhanden sind, ändert sich die Farbe der Bildlaufleiste von Grau zu Blau, als wolle sie anzeigen, dass die Bildlaufleiste fokussiert ist. Wenn Sie möchten, können Sie einen ähnlichen Effekt implementieren.

Die MotionEvents sind mit Ausnahme der Quelle dieselben wie die, die durch ein Scrollrad einer Maus generiert werden.

Direkter Manipulationsmodus

Normalerweise navigieren Sie durch Stupsen und Drehen durch die Benutzeroberfläche, während das Drücken der Mitteltaste ausgeführt wird. Dies ist jedoch nicht immer der Fall. Wenn ein Benutzer beispielsweise die Alarmlautstärke anpassen möchte, kann er mit dem Drehregler zum Lautstärkeregler navigieren, die mittlere Taste drücken, den Controller drehen, um die Alarmlautstärke anzupassen, und dann die Zurück-Taste drücken, um zur Navigation zurückzukehren . Dies wird als Direktmanipulationsmodus (DM) bezeichnet. In diesem Modus wird der Drehregler nicht zum Navigieren, sondern zur direkten Interaktion mit der Ansicht verwendet.

Implementieren Sie DM auf eine von zwei Arten. Wenn Sie nur die Drehung verarbeiten müssen und die Ansicht, die Sie bearbeiten möchten, entsprechend auf ACTION_SCROLL_FORWARD und ACTION_SCROLL_BACKWARD AccessibilityEvent 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 Auto, Android 12 )
Ihre App sollte DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) aufrufen. RotaryService erkennt, wenn sich der Benutzer im DM-Modus befindet, und wechselt in den DM-Modus, wenn der Benutzer die mittlere Taste drückt, während eine Ansicht fokussiert ist. Im DM-Modus führen Rotationen ACTION_SCROLL_FORWARD oder ACTION_SCROLL_BACKWARD aus und verlassen den DM-Modus, wenn der Benutzer die Zurück-Taste drückt. Der einfache Mechanismus schaltet den ausgewählten Status der Ansicht beim Aufrufen und Verlassen des DM-Modus um.

Um einen visuellen Hinweis darauf zu geben, dass sich der Benutzer im DM-Modus befindet, lassen Sie Ihre Ansicht bei Auswahl anders erscheinen. Ändern Sie beispielsweise den Hintergrund, wenn android:state_selected true ist.

Fortschrittlicher Mechanismus

Die App bestimmt, wann RotaryService in den DM-Modus wechselt und diesen verlässt. Für ein konsistentes Benutzererlebnis sollte durch Drücken der mittleren Taste bei fokussierter DM-Ansicht der DM-Modus aufgerufen werden und durch Drücken der Zurück-Taste der DM-Modus verlassen werden. Wenn die mittlere Taste und/oder Nudge nicht verwendet werden, können sie alternative Möglichkeiten zum Verlassen des DM-Modus sein. Bei Apps wie Maps kann eine Schaltfläche zur Darstellung von DM verwendet werden, um in den DM-Modus zu gelangen.

Um den erweiterten DM-Modus zu unterstützen, eine Ansicht:

  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 warten, um den DM-Modus zu verlassen, wobei jeweils DirectManipulationHelper.enableDirectManipulationMode() aufgerufen wird. Um auf diese Ereignisse zu warten, führen Sie einen der folgenden Schritte aus:
    • Registrieren Sie einen OnKeyListener .
    • oder,
    • Erweitern Sie die Ansicht und überschreiben Sie dann deren Methode dispatchKeyEvent() .
  2. SOLLTE auf Nudge-Ereignisse ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT oder KEYCODE_DPAD_RIGHT ) warten, wenn die Ansicht Nudges verarbeiten soll.
  3. SOLLTE auf MotionEvent s hören und die Rotationsanzahl in AXIS_SCROLL abrufen, wenn die Ansicht Rotationen verarbeiten möchte. Dafür gibt es mehrere Möglichkeiten:
    1. Registrieren Sie einen OnGenericMotionListener .
    2. Erweitern Sie die Ansicht und überschreiben Sie deren Methode dispatchTouchEvent() .
  4. Um zu vermeiden, dass Sie im DM-Modus stecken bleiben, MÜSSEN Sie den DM-Modus verlassen, wenn das Fragment oder die Aktivität, zu der die Ansicht gehört, nicht interaktiv ist.
  5. SOLLte einen visuellen Hinweis geben, der darauf hinweist, dass sich die Ansicht im DM-Modus befindet.

Nachfolgend finden Sie ein Beispiel einer benutzerdefinierten Ansicht, die den DM-Modus zum Schwenken und Zoomen einer Karte verwendet:

/** 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 .

Aktivitätsansicht

Bei Verwendung einer ActivityView:

  • Die ActivityView sollte nicht fokussierbar sein.
  • ( Android 11 QPR3, Android 11 Car, veraltet in Android 11 )
    Der Inhalt der ActivityView MUSS eine FocusParkingView als erste fokussierbare Ansicht enthalten und sein app:shouldRestoreFocus Attribut MUSS false sein.
  • Der Inhalt der ActivityView sollte keine android:focusByDefault Ansichten haben.

Für den Benutzer sollten ActivityViews keine Auswirkungen auf die Navigation haben, mit der Ausnahme, dass Fokusbereiche sich nicht über ActivityViews erstrecken können. Mit anderen Worten: Sie können keinen einzelnen Fokusbereich haben, der Inhalte innerhalb und außerhalb einer ActivityView enthält. Wenn Sie Ihrer ActivityView keine FocusAreas hinzufügen, wird der Stamm der Ansichtshierarchie in der ActivityView als impliziter Fokusbereich betrachtet.

Tasten, die funktionieren, wenn sie gedrückt gehalten werden

Die meisten Schaltflächen lösen beim Klicken eine Aktion aus. Einige Tasten funktionieren stattdessen, wenn sie gedrückt gehalten werden. Beispielsweise funktionieren die Tasten „Schneller Vorlauf“ und „Rücklauf“ normalerweise, wenn sie gedrückt gehalten werden. Damit solche Tasten den Drehknopf unterstützen, achten Sie wie folgt auf 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 plant die Ausführung nach einer Verzögerung.

Touch-Modus

Benutzer können einen Drehregler verwenden, um auf zwei Arten mit der Headunit in einem Auto zu interagieren: entweder durch Verwendung des Drehreglers oder durch Berühren des Bildschirms. Bei Verwendung des Drehreglers wird eine der fokussierbaren Ansichten hervorgehoben. Beim Berühren des Bildschirms wird keine Fokushervorhebung angezeigt. Der Benutzer kann jederzeit zwischen diesen Eingabemodi wechseln:

  • Drehen → berühren. Wenn der Benutzer den Bildschirm berührt, verschwindet die Fokushervorhebung.
  • Berühren Sie → drehen. Wenn der Benutzer die Mitteltaste bewegt, dreht oder drückt, wird die Fokushervorhebung angezeigt.

Die Zurück- und Home-Tasten haben keinen Einfluss auf den Eingabemodus.

Rotary huckepack auf Androids bestehendem Konzept des Touch-Modus . Mit View.isInTouchMode() können Sie ermitteln, welchen Eingabemodus der Benutzer verwendet. Sie können OnTouchModeChangeListener verwenden, um auf Änderungen zu warten. Damit können Sie Ihre Benutzeroberfläche zwar an den aktuellen Eingabemodus anpassen, aber vermeiden Sie größere Änderungen, da diese beunruhigend sein können.

Fehlerbehebung

In einer für die Berührung konzipierten App sind häufig verschachtelte fokussierbare Ansichten vorhanden. Beispielsweise könnte es ein FrameLayout um einen ImageButton geben, die beide fokussierbar sind. Dies schadet der Touch-Funktion nicht, kann jedoch zu einer schlechten Benutzererfahrung bei der Rotation führen, da der Benutzer den Controller zweimal drehen muss, um zur nächsten interaktiven Ansicht zu gelangen. Für ein gutes Nutzererlebnis empfiehlt Google, entweder die Außenansicht oder die Innenansicht fokussierbar zu machen, jedoch nicht beide.

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

  • Die Taste oder der Schalter wird (kurzzeitig oder auf unbestimmte Zeit) deaktiviert, weil die Taste gedrückt wird. In beiden Fällen gibt es zwei Möglichkeiten, dieses Problem anzugehen:
    • Behalten Sie den Status android:enabled bei „ true “ und verwenden Sie einen benutzerdefinierten Status, um die Schaltfläche oder den Schalter auszublenden, wie unter „Benutzerdefinierter Status“ beschrieben.
    • Verwenden Sie einen Behälter, um die Taste oder den Schalter zu umgeben und den Behälter anstelle der Taste oder des Schalters fokussierbar zu machen. (Der Click-Listener muss sich im Container befinden.)
  • Der Knopf oder Schalter wird ausgetauscht. Beispielsweise kann die Aktion, die beim Drücken der Taste oder beim Umschalten des Schalters ausgeführt wird, eine Aktualisierung der verfügbaren Aktionen auslösen, wodurch neue Tasten die vorhandenen Tasten ersetzen. Es gibt zwei Möglichkeiten, dies zu beheben:
    • Anstatt eine neue Schaltfläche oder einen neuen Schalter zu erstellen, legen Sie das Symbol und/oder den Text der vorhandenen Schaltfläche oder des vorhandenen Schalters fest.
    • Fügen Sie wie oben einen fokussierbaren Behälter um die Taste oder den Schalter hinzu.

RotaryPlayground

RotaryPlayground ist eine Referenz-App für Rotary. Erfahren Sie hier, wie Sie Rotationsfunktionen in Ihre Apps integrieren. RotaryPlayground ist in Emulator-Builds und in Builds für Geräte enthalten, auf denen Android Automotive OS (AAOS) ausgeführt wird.

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

Die RotaryPlayground App zeigt auf der linken Seite die folgenden Registerkarten:

  • Karten. Testen Sie das Navigieren in Fokusbereichen, das Überspringen nicht fokussierbarer Elemente und die Texteingabe.
  • Direkte Manipulation. Testen Sie Widgets, die den einfachen und erweiterten Direktmanipulationsmodus unterstützen. Diese Registerkarte dient speziell der direkten Manipulation innerhalb des App-Fensters.
  • Manipulation der Sys-Benutzeroberfläche. Testen Sie Widgets, die die direkte Manipulation in Systemfenstern unterstützen, in denen nur der einfache direkte Manipulationsmodus unterstützt wird.
  • Netz. Testen Sie die Z-Muster-Rotationsnavigation mit Scrollen.
  • Benachrichtigung. Testen Sie das Ein- und Ausschalten von Heads-up-Benachrichtigungen.
  • Scrollen. Testen Sie das Scrollen durch eine Mischung aus fokussierbaren und nicht fokussierbaren Inhalten.
  • WebView. Testen Sie die Navigation durch Links in einem WebView .
  • Benutzerdefinierter FocusArea . Testen Sie die FocusArea -Anpassung:
    • Rundum.
    • android:focusedByDefault und app:defaultFocus
    • .
    • Explizite Nudge-Ziele.
    • Nudge-Verknüpfungen.
    • FocusArea ohne fokussierbare Ansichten.