Das folgende Material richtet sich an App-Entwickler.
Damit Ihre App Rotation unterstützt, MÜSSEN Sie:
- Platzieren Sie ein
FocusParkingView
im jeweiligen Aktivitätslayout. - Stellen Sie sicher, dass die Ansichten fokussierbar sind (oder nicht).
- Verwenden Sie
FocusArea
s, um alle Ihre fokussierbaren Ansichten mit Ausnahme vonFocusParkingView
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:
- Tippen Sie auf die drei Punkte unten in der Symbolleiste:
- Wählen Sie im Fenster „Erweiterte Steuerung“ die Option „Autodreh“ aus:
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
odereng
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:
- 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, sodassRotaryService
den Fokus darauf parken kann, um die Fokushervorhebung zu entfernen. - Wenn im aktuellen Fenster nur ein
FocusArea
vorhanden ist, führt das Drehen des Controllers imFocusArea
dazu, dassRotaryService
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. WennRotaryService
feststellt, dass es sich bei dem Fokusziel um einFocusParkingView
handelt, kann es feststellen, dass ein Umlauf bevorsteht, und an diesem Punkt den Umlauf vermeiden, indem der Fokus nicht verschoben wird. - 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:
- So fügen Sie Ihrer Ansicht ein benutzerdefiniertes Attribut hinzu. Um beispielsweise einen benutzerdefinierten Status
state_rotary_enabled
“ zur AnsichtsklasseCustomView
hinzuzufügen, verwenden Sie:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- 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; }
- 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);
- Ü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; }
- 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. - Um die Schaltfläche deaktiviert erscheinen zu lassen, verwenden Sie im zeichnbaren Hintergrund Ihrer Ansicht
app:state_rotary_enabled
anstelle vonandroid:state_enabled
. Wenn Sie es noch nicht haben, müssen Sie Folgendes hinzufügen:xmlns:app="http://schemas.android.com/apk/res-auto"
- Wenn Ihre Ansicht in irgendeinem Layout deaktiviert ist, ersetzen Sie
android:enabled="false"
durchapp:state_rotary_enabled="false"
und fügen Sie dann denapp
Namespace wie oben hinzu. - Wenn Ihre Ansicht programmgesteuert deaktiviert ist, ersetzen Sie Aufrufe von
setEnabled()
durch Aufrufe vonsetRotaryEnabled()
.
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:
- Bei der Verarbeitung von Rotations- und Nudge-Aktionen sucht
RotaryService
in der Ansichtshierarchie nach Instanzen vonFocusArea
. - Beim Empfang eines Rotationsereignisses verschiebt
RotaryService
den Fokus auf eine andere Ansicht, die den Fokus im selbenFocusArea
übernehmen kann. - 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 stattdessenapp:wrapAround
(siehe unten), um eine Schleife zu erstellen. -
android:focusedByDefault
können App-Entwickler die Standardfokusansicht im Fenster festlegen. Verwenden Sie dieses Attribut undapp:defaultFocus
(siehe unten) NICHT im selbenFocusArea
.
FocusArea
definiert auch einige Attribute, um die Rotationsnavigation anzupassen. Implizite Fokusbereiche können mit diesen Attributen nicht angepasst werden.
- ( 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 dieserFocusArea
wechselt. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
app:defaultFocusOverridesHistory
kann auftrue
gesetzt werden, damit die oben angegebene Ansicht den Fokus erhält, auch wenn mit dem Verlauf angegeben wird, dass auf eine andere Ansicht in diesemFocusArea
der Fokus gelegt wurde. - ( Android 12 )
Verwenden Sieapp:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
undapp: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
undapp:nudgeShortcutDirection
unterstützten nur eine Nudge-Verknüpfung. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Damit die Rotation in diesemFocusArea
umlaufen kann, kannapp:wrapAround
auftrue
gesetzt werden. Dies wird am häufigsten verwendet, wenn Ansichten kreisförmig oder oval angeordnet sind. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Um den Abstand der Hervorhebung in diesemFocusArea
anzupassen, verwenden Sieapp:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
undapp:highlightPaddingVertical
. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Um die wahrgenommenen Grenzen dieserFocusArea
anzupassen, um ein Schubziel zu finden, verwenden Sieapp:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
,app:horizontalBoundOffset
undapp:verticalBoundOffset
. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Um die ID einer benachbartenFocusArea
(oder Bereiche) in den angegebenen Richtungen explizit anzugeben, verwenden Sieapp:nudgeLeft
,app:nudgeRight
,app:nudgeUp
undapp: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:
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:
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:
- ( 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 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 entwederAXIS_VSCROLL
oderAXIS_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:
- ( 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 einKEYCODE_BACK
Ereignis warten, um den DM-Modus zu verlassen, wobei jeweilsDirectManipulationHelper.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()
.
- Registrieren Sie einen
- SOLLTE auf Nudge-Ereignisse (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
oderKEYCODE_DPAD_RIGHT
) warten, wenn die Ansicht Nudges verarbeiten soll. - SOLLTE auf
MotionEvent
s hören und die Rotationsanzahl inAXIS_SCROLL
abrufen, wenn die Ansicht Rotationen verarbeiten möchte. Dafür gibt es mehrere Möglichkeiten:- Registrieren Sie einen
OnGenericMotionListener
. - Erweitern Sie die Ansicht und überschreiben Sie deren Methode
dispatchTouchEvent()
.
- Registrieren Sie einen
- 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.
- 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 derActivityView
MUSS eineFocusParkingView
als erste fokussierbare Ansicht enthalten und seinapp:shouldRestoreFocus
Attribut MUSSfalse
sein. - Der Inhalt der
ActivityView
sollte keineandroid: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.)
- Behalten Sie den Status
- 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 dieFocusArea
-Anpassung:- Rundum.
-
android:focusedByDefault
undapp:defaultFocus
. - Explizite Nudge-Ziele.
- Nudge-Verknüpfungen.
-
FocusArea
ohne fokussierbare Ansichten.