Il seguente materiale è destinato agli sviluppatori di app.
Per far sì che la tua app supporti la rotazione, DEVI:
- Posiziona un
FocusParkingView
nel rispettivo layout dell'attività. - Garantire le visualizzazioni che sono (o non sono) focalizzabili.
- Utilizza
FocusArea
s per racchiudere tutte le visualizzazioni focalizzabili, ad eccezione diFocusParkingView
.
Ognuna di queste attività è descritta in dettaglio di seguito, dopo aver configurato l'ambiente per sviluppare app abilitate per la rotazione.
Configura un controller rotativo
Prima di poter iniziare a sviluppare app abilitate per la rotazione, è necessario un controller rotante o un supporto. Hai le opzioni descritte di seguito.
Emulatore
source build/envsetup.sh && lunch car_x86_64-userdebug m -j emulator -wipe-data -no-snapshot -writable-system
Puoi anche usare aosp_car_x86_64-userdebug
.
Per accedere al controller rotativo emulato:
- Tocca i tre punti nella parte inferiore della barra degli strumenti:
- Seleziona Rotante auto nella finestra dei controlli estesi:
Tastiera USB
- Collega una tastiera USB al tuo dispositivo che esegue il sistema operativo Android Automotive (AAOS). In alcuni casi, ciò impedisce la visualizzazione della tastiera su schermo.
- Utilizzare un
userdebug
o una buildeng
. - Abilita il filtraggio degli eventi chiave:
adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
- Consulta la tabella seguente per trovare la chiave corrispondente per ciascuna azione:
Chiave Azione rotatoria Q Ruotare in senso antiorario E Ruotare in senso orario UN Spingi a sinistra D Spingi a destra W Spingi verso l'alto S Spingi verso il basso F o virgola Pulsante centrale R o Esc Tasto indietro
Comandi dell'ADB
È possibile utilizzare i comandi car_service
per inserire eventi di input rotanti. Questi comandi possono essere eseguiti su dispositivi che eseguono il sistema operativo Android Automotive (AAOS) o su un emulatore.
comandi car_service | Ingresso rotativo |
---|---|
adb shell cmd car_service inject-rotary | Ruotare in senso antiorario |
adb shell cmd car_service inject-rotary -c true | Ruotare in senso orario |
adb shell cmd car_service inject-rotary -dt 100 50 | Ruota in senso antiorario più volte (100 ms fa e 50 ms fa) |
adb shell cmd car_service inject-key 282 | Spingi a sinistra |
adb shell cmd car_service inject-key 283 | Spingi a destra |
adb shell cmd car_service inject-key 280 | Spingi verso l'alto |
adb shell cmd car_service inject-key 281 | Spingi verso il basso |
adb shell cmd car_service inject-key 23 | Clic sul pulsante centrale |
adb shell input keyevent inject-key 4 | Fare clic sul pulsante Indietro |
Controller rotativo OEM
Quando l'hardware del controller rotativo è attivo e funzionante, questa è l'opzione più realistica. È particolarmente utile per testare la rotazione veloce.
FocusParkingView
FocusParkingView
è una visualizzazione trasparente nella libreria dell'interfaccia utente dell'auto (car-ui-library) . RotaryService
lo utilizza per supportare la navigazione del controller rotante. FocusParkingView
deve essere la prima vista attivabile nel layout. Deve essere posizionato all'esterno di tutte le FocusArea
. Ogni finestra deve avere un FocusParkingView
. Se stai già utilizzando il layout di base car-ui-library, che contiene un FocusParkingView
, non è necessario aggiungere un altro FocusParkingView
. Di seguito è mostrato un esempio di 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>
Ecco i motivi per cui hai bisogno di FocusParkingView
:
- Android non cancella automaticamente il focus quando il focus è impostato in un'altra finestra. Se provi a cancellare lo stato attivo nella finestra precedente, Android rimette a fuoco una vista in quella finestra, il che si traduce in due finestre focalizzate contemporaneamente. L'aggiunta di
FocusParkingView
a ciascuna finestra può risolvere questo problema. Questa visualizzazione è trasparente e la sua evidenziazione di messa a fuoco predefinita è disabilitata, in modo che sia invisibile all'utente, indipendentemente dal fatto che sia focalizzata o meno. Può prendere la messa a fuoco in modo cheRotaryService
possa parcheggiarla su di essa per rimuovere l'evidenziazione della messa a fuoco. - Se è presente una sola
FocusArea
nella finestra corrente, la rotazione del controller nellaFocusArea
fa sì cheRotaryService
sposti il focus dalla vista a destra alla vista a sinistra (e viceversa). L'aggiunta di questa visualizzazione a ciascuna finestra può risolvere il problema. QuandoRotaryService
determina che l'obiettivo del focus è unFocusParkingView
, può determinare che sta per verificarsi un wrap-around e a quel punto evita il wrap-around non spostando il focus. - Quando la manopola avvia un'app, Android mette a fuoco la prima vista focalizzabile, che è sempre
FocusParkingView
.FocusParkingView
determina la visualizzazione ottimale su cui concentrarsi e quindi applica lo stato attivo.
Viste focalizzabili
RotaryService
si basa sul concetto esistente di visualizzazione focalizzata del framework Android, risalente a quando i telefoni avevano tastiere fisiche e D-pad. L'attributo android:nextFocusForward
esistente viene riproposto per Rotary (vedere Personalizzazione FocusArea ), ma android:nextFocusLeft
, android:nextFocusRight
, android:nextFocusUp
e android:nextFocusDown
non lo sono.
RotaryService
si concentra solo su punti di vista focalizzabili. Alcune visualizzazioni, come Button
s, sono generalmente focalizzabili. Altri, come TextView
e ViewGroup
, di solito non lo sono. Le visualizzazioni selezionabili sono automaticamente selezionabili e le visualizzazioni sono automaticamente selezionabili quando dispongono di un listener di clic. Se questa logica automatica determina la focalizzabilità desiderata, non è necessario impostare esplicitamente la focalizzabilità della vista. Se la logica automatica non produce la focalizzabilità desiderata, imposta l'attributo android:focusable
su true
o false
oppure imposta a livello di codice la focalizzabilità della vista con View.setFocusable(boolean)
. Affinché RotaryService
possa concentrarsi su questo, una vista DEVE soddisfare i seguenti requisiti:
- Focalizzabile
- Abilitato
- Visibile
- Avere valori diversi da zero per larghezza e altezza
Se una vista non soddisfa tutti questi requisiti, ad esempio un pulsante attivabile ma disabilitato, l'utente non può utilizzare la manopola per focalizzarla. Se vuoi concentrarti sulle visualizzazioni disabilitate, considera l'utilizzo di uno stato personalizzato anziché android:state_enabled
per controllare come appare la visualizzazione senza indicare che Android dovrebbe considerarla disabilitata. La tua app può informare l'utente del motivo per cui la visualizzazione è disabilitata quando viene toccata. La sezione successiva spiega come eseguire questa operazione.
Stato personalizzato
Per aggiungere uno stato personalizzato:
- Per aggiungere un attributo personalizzato alla tua vista. Ad esempio, per aggiungere uno stato personalizzato
state_rotary_enabled
alla classe di visualizzazioneCustomView
, utilizzare:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- Per tenere traccia di questo stato, aggiungi una variabile di istanza alla tua vista insieme ai metodi di accesso:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- Per leggere il valore del tuo attributo quando viene creata la vista:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- Nella classe di visualizzazione, sovrascrivi il metodo
onCreateDrawableState()
e quindi aggiungi lo stato personalizzato, quando appropriato. Ad esempio:@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; }
- Fai in modo che il gestore dei clic della tua vista funzioni in modo diverso a seconda del suo stato. Ad esempio, il gestore dei clic potrebbe non fare nulla o potrebbe visualizzare un avviso quando
mRotaryEnabled
èfalse
. - Per far sì che il pulsante appaia disabilitato, nello sfondo disegnabile della tua vista, usa
app:state_rotary_enabled
invece diandroid:state_enabled
. Se non lo hai già, dovrai aggiungere:xmlns:app="http://schemas.android.com/apk/res-auto"
- Se la visualizzazione è disabilitata in qualsiasi layout, sostituisci
android:enabled="false"
conapp:state_rotary_enabled="false"
e quindi aggiungi lo spazio dei nomiapp
, come sopra. - Se la vista è disabilitata a livello di codice, sostituisci le chiamate a
setEnabled()
con le chiamate asetRotaryEnabled()
.
Area di messa a fuoco
Utilizza FocusAreas
per suddividere le visualizzazioni focalizzabili in blocchi per semplificare la navigazione ed essere coerenti con altre app. Ad esempio, se la tua app ha una barra degli strumenti, la barra degli strumenti dovrebbe trovarsi in una FocusArea
separata dal resto della tua app. Anche le barre delle schede e gli altri elementi di navigazione dovrebbero essere separati dal resto dell'app. Gli elenchi di grandi dimensioni dovrebbero generalmente avere una propria FocusArea
. In caso contrario, gli utenti dovranno ruotare l'intero elenco per accedere ad alcune visualizzazioni.
FocusArea
è una sottoclasse di LinearLayout
nella libreria car-ui. Quando questa funzione è abilitata, FocusArea
disegna un'evidenziazione quando uno dei suoi discendenti è focalizzato. Per ulteriori informazioni, consulta Personalizzazione dell'evidenziazione del focus .
Quando crei un blocco di navigazione nel file di layout, se intendi utilizzare LinearLayout
come contenitore per quel blocco, utilizza invece FocusArea
. Altrimenti, avvolgi il blocco in un FocusArea
.
NON nidificare una FocusArea
in un'altra FocusArea
. Ciò porta a un comportamento di navigazione indefinito. Assicurati che tutte le visualizzazioni focalizzabili siano nidificate all'interno di FocusArea
.
Di seguito è mostrato un esempio di 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
funziona come segue:
- Durante la gestione delle azioni di rotazione e spostamento,
RotaryService
cerca istanze diFocusArea
nella gerarchia della vista. - Quando si riceve un evento di rotazione,
RotaryService
sposta lo stato attivo su un'altra vista che può essere attiva nella stessaFocusArea
. - Quando si riceve un evento di sollecito,
RotaryService
sposta lo stato attivo su un'altra vista che può concentrarsi in un'altraFocusArea
(in genere adiacente).
Se non includi alcuna FocusAreas
nel layout, la vista radice viene trattata come un'area di focus implicita. L'utente non può spostarsi nell'app. Invece, ruoteranno attraverso tutte le visualizzazioni focalizzabili, il che potrebbe essere adeguato per le finestre di dialogo.
Personalizzazione dell'area focus
È possibile utilizzare due attributi di visualizzazione standard per personalizzare la navigazione rotativa:
-
android:nextFocusForward
consente agli sviluppatori di app di specificare l'ordine di rotazione in un'area di interesse. Questo è lo stesso attributo utilizzato per controllare l'ordine di tabulazione per la navigazione da tastiera. NON utilizzare questo attributo per creare un loop. Utilizza inveceapp:wrapAround
(vedi sotto) per creare un loop. -
android:focusedByDefault
consente agli sviluppatori di app di specificare la visualizzazione di messa a fuoco predefinita nella finestra. NON utilizzare questo attributo eapp:defaultFocus
(vedi sotto) nella stessaFocusArea
.
FocusArea
definisce anche alcuni attributi per personalizzare la navigazione rotativa. Le aree di interesse implicite non possono essere personalizzate con questi attributi.
- ( Android 11 QPR3, Android 11 Auto, Android 12 )
app:defaultFocus
può essere utilizzato per specificare l'ID di una vista discendente focalizzabile, su cui dovrebbe essere focalizzata quando l'utente sposta questoFocusArea
. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
app:defaultFocusOverridesHistory
può essere impostato sutrue
per mettere a fuoco la vista specificata sopra anche se con la cronologia per indicare che è stata focalizzata un'altra vista in questaFocusArea
. - ( Android 12 )
Utilizzaapp:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
eapp:nudgeDownShortcut
per specificare l'ID di una vista discendente focalizzabile, su cui dovrebbe essere focalizzata quando l'utente si sposta in una determinata direzione. Per ulteriori informazioni, consulta il contenuto delle scorciatoie per i solleciti di seguito.( Android 11 QPR3, Android 11 Car, obsoleto in Android 12 )
app:nudgeShortcut
eapp:nudgeShortcutDirection
supportavano solo una scorciatoia nudge. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Per consentire il ritorno a capo della rotazione in questaFocusArea
,app:wrapAround
può essere impostato sutrue
. Questo viene in genere utilizzato quando le viste sono disposte in un cerchio o in un ovale. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Per regolare la spaziatura dell'evidenziazione in questaFocusArea
, utilizzaapp:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
eapp:highlightPaddingVertical
. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Per regolare i limiti percepiti di questaFocusArea
per trovare un obiettivo di spinta, utilizzareapp:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
,app:horizontalBoundOffset
eapp:verticalBoundOffset
. - ( Android 11 QPR3, Android 11 Auto, Android 12 )
Per specificare esplicitamente l'ID di unaFocusArea
(o di aree) adiacenti nelle direzioni fornite, utilizzareapp:nudgeLeft
,app:nudgeRight
,app:nudgeUp
eapp:nudgeDown
. Utilizzare questo quando la ricerca geometrica utilizzata per impostazione predefinita non trova il target desiderato.
Il nudging solitamente consente di spostarsi tra le FocusArea. Tuttavia, con le scorciatoie di spostamento, lo spostamento a volte consente di navigare prima all'interno di una FocusArea
in modo che l'utente potrebbe dover eseguire due spostamenti per passare alla successiva FocusArea
. Le scorciatoie di spinta sono utili quando FocusArea
contiene un lungo elenco seguito da un pulsante di azione mobile , come nell'esempio seguente:
Senza la scorciatoia di spinta, l'utente dovrebbe ruotare l'intero elenco per raggiungere il FAB.
Personalizzazione dell'evidenziazione del focus
Come notato in precedenza, RotaryService
si basa sul concetto esistente di visualizzazione focalizzata del framework Android. Quando l'utente ruota e sposta, RotaryService
sposta l'attenzione, focalizzando una vista e sfocandone un'altra. In Android, quando una vista è focalizzata, se la vista:
- Ha specificato il proprio focus evidenziato, Android disegna l'evidenziazione del focus della vista.
- Non specifica un'evidenziazione dello stato attivo e l'evidenziazione dello stato attivo predefinita non è disabilitata, Android disegna l'evidenziazione dello stato attivo predefinita per la vista.
Le app progettate per il tocco in genere non specificano le evidenziazioni di messa a fuoco appropriate.
L'evidenziazione dello stato attivo predefinita è fornita dal framework Android e può essere sovrascritta dall'OEM. Gli sviluppatori di app lo ricevono quando il tema che stanno utilizzando deriva da Theme.DeviceDefault
.
Per un'esperienza utente coerente, affidati all'evidenziazione dello stato attivo predefinito quando possibile. Se hai bisogno di un'evidenziazione di messa a fuoco di forma personalizzata (ad esempio, rotonda o a forma di pillola) o se stai utilizzando un tema non derivato da Theme.DeviceDefault
, utilizza le risorse car-ui-library per specificare la tua evidenziazione di messa a fuoco personalizzata per ogni vista.
Per specificare un'evidenziazione di messa a fuoco personalizzata per una vista, modificare il disegno di sfondo o di primo piano della vista in un disegno che differisce quando viene messa a fuoco la vista. In genere, cambieresti lo sfondo. Il seguente elemento disegnabile, se utilizzato come sfondo per una vista quadrata, produce un'evidenziazione del fuoco circolare:
<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 ) I riferimenti alle risorse in grassetto nell'esempio precedente identificano le risorse definite dalla libreria car-ui. L'OEM li sovrascrive per essere coerenti con l'evidenziazione di messa a fuoco predefinita specificata. Ciò garantisce che il colore dell'evidenziazione dello stato attivo, la larghezza del tratto e così via non cambino quando l'utente passa da una vista con un'evidenziazione dello stato attivo personalizzata a una vista con l'evidenziazione dello stato attivo predefinita. L'ultimo elemento è un'increspatura utilizzata per il tatto. I valori predefiniti utilizzati per le risorse in grassetto vengono visualizzati come segue:
Inoltre, viene richiesto un focus personalizzato quando a un pulsante viene assegnato un colore di sfondo a tinta unita per portarlo all'attenzione dell'utente, come nell'esempio seguente. Ciò può rendere difficile vedere l'evidenziazione della messa a fuoco. In questa situazione, specifica un'evidenziazione della messa a fuoco personalizzata utilizzando i colori secondari :
- ( 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
Per esempio:
Concentrato, non pressato | Concentrato, pressato |
Scorrimento rotatorio
Se la tua app utilizza RecyclerView
, DOVREBBE utilizzare invece CarUiRecyclerView
. Ciò garantisce che la tua interfaccia utente sia coerente con le altre perché la personalizzazione di un OEM si applica a tutti i CarUiRecyclerView
.
Se gli elementi nella tua lista sono tutti focalizzabili, non devi fare nient'altro. La navigazione rotativa sposta il focus sugli elementi nell'elenco e l'elenco scorre per rendere visibile l'elemento appena focalizzato.
( Android 11 QPR3, Android 11 Auto, Android 12 )
Se è presente una combinazione di elementi focalizzabili e non focalizzabili, o se tutti gli elementi non sono focalizzabili, è possibile abilitare lo scorrimento rotatorio, che consente all'utente di utilizzare la manopola per scorrere gradualmente l'elenco senza saltare gli elementi non focalizzabili. Per abilitare lo scorrimento rotatorio, imposta l'attributo app:rotaryScrollEnabled
su true
.
( Android 11 QPR3, Android 11 Auto, Android 12 )
Puoi abilitare lo scorrimento rotatorio in qualsiasi vista scorrevole, incluso av CarUiRecyclerView
, con il metodo setRotaryScrollEnabled()
in CarUiUtils
. Se lo fai, devi:
- Rendere focalizzabile la vista scorrevole in modo che possa essere focalizzata quando nessuna delle sue viste discendenti focalizzabili è visibile,
- Disabilita l'evidenziazione dello stato attivo predefinito sulla vista scorrevole chiamando
setDefaultFocusHighlightEnabled(false)
in modo che la vista scorrevole non sembri essere focalizzata, - Assicurati che la vista scorrevole sia focalizzata prima dei suoi discendenti chiamando
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
. - Ascolta MotionEvents con
SOURCE_ROTARY_ENCODER
eAXIS_VSCROLL
oAXIS_HSCROLL
per indicare la distanza da scorrere e la direzione (attraverso il segno).
Quando lo scorrimento rotante è abilitato su un CarUiRecyclerView
e l'utente ruota in un'area in cui non sono presenti visualizzazioni focalizzabili, la barra di scorrimento cambia da grigia a blu, come per indicare che la barra di scorrimento è focalizzata. Se lo desideri, puoi implementare un effetto simile.
I MotionEvents sono gli stessi generati dalla rotella di scorrimento del mouse, ad eccezione della sorgente.
Modalità di manipolazione diretta
Normalmente, gli spostamenti e la rotazione navigano attraverso l'interfaccia utente, mentre la pressione del pulsante centrale agisce, anche se non è sempre così. Ad esempio, se un utente desidera regolare il volume della sveglia, potrebbe utilizzare la manopola di controllo per spostarsi sul cursore del volume, premere il pulsante centrale, ruotare il controller per regolare il volume della sveglia, quindi premere il pulsante Indietro per tornare alla navigazione. . Questa viene definita modalità di manipolazione diretta (DM) . In questa modalità, la manopola viene utilizzata per interagire direttamente con la vista anziché per navigare.
Implementare DM in due modi. Se è necessario gestire solo la rotazione e la vista che si desidera manipolare risponde in modo appropriato agli AccessibilityEvent
ACTION_SCROLL_FORWARD
e ACTION_SCROLL_BACKWARD
, utilizzare il meccanismo semplice . Altrimenti utilizza il meccanismo avanzato .
Il meccanismo semplice è l'unica opzione nelle finestre di sistema; le app possono utilizzare entrambi i meccanismi.
Meccanismo semplice
( Android 11 QPR3, Android 11 Auto, Android 12 )
La tua app dovrebbe chiamare DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
. RotaryService
riconosce quando l'utente è in modalità DM e accede alla modalità DM quando l'utente preme il pulsante centrale mentre è focalizzata una vista. In modalità DM, le rotazioni eseguono ACTION_SCROLL_FORWARD
o ACTION_SCROLL_BACKWARD
e escono dalla modalità DM quando l'utente preme il pulsante Indietro. Il semplice meccanismo alterna lo stato selezionato della vista quando si entra e si esce dalla modalità DM.
Per fornire un segnale visivo che l'utente è in modalità DM, fai in modo che la tua visualizzazione appaia diversa quando selezionata. Ad esempio, cambia lo sfondo quando android:state_selected
è true
.
Meccanismo avanzato
L'app determina quando RotaryService
entra ed esce dalla modalità DM. Per un'esperienza utente coerente, premendo il pulsante centrale con una vista DM focalizzata si dovrebbe entrare in modalità DM e il pulsante Indietro dovrebbe uscire dalla modalità DM. Se il pulsante centrale e/o la spinta non vengono utilizzati, possono rappresentare modi alternativi per uscire dalla modalità DM. Per app come Mappe, è possibile utilizzare un pulsante per rappresentare DM per accedere alla modalità DM.
Per supportare la modalità DM avanzata, una visualizzazione:
- ( Android 11 QPR3, Android 11 Car, Android 12 ) DEVE attendere un evento
KEYCODE_DPAD_CENTER
per accedere alla modalità DM e attendere un eventoKEYCODE_BACK
per uscire dalla modalità DM, chiamandoDirectManipulationHelper.enableDirectManipulationMode()
in ogni caso. Per ascoltare questi eventi, effettuare una delle seguenti operazioni:- Registra un
OnKeyListener
. O, - Estendi la vista e quindi sovrascrivi il relativo metodo
dispatchKeyEvent()
.
- Registra un
- DOVREBBE ascoltare gli eventi di sollecito (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
oKEYCODE_DPAD_RIGHT
) se la vista deve gestire i solleciti. - DOVREBBE ascoltare
MotionEvent
e ottenere il conteggio delle rotazioni inAXIS_SCROLL
se la vista desidera gestire la rotazione. Esistono diversi modi per farlo:- Registra un
OnGenericMotionListener
. - Estendi la visualizzazione e sovrascrivi il relativo metodo
dispatchTouchEvent()
.
- Registra un
- Per evitare di rimanere bloccati in modalità DM, DEVE uscire dalla modalità DM quando il frammento o l'attività a cui appartiene la vista non è interattivo.
- DOVREBBE fornire un segnale visivo per indicare che la vista è in modalità DM.
Di seguito viene fornito un esempio di visualizzazione personalizzata che utilizza la modalità DM per eseguire la panoramica e lo zoom di una mappa:
/** 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(); }
Ulteriori esempi possono essere trovati nel progetto RotaryPlayground
.
Visualizzazione attività
Quando si utilizza ActivityView:
-
ActivityView
non dovrebbe essere focalizzabile. - ( Android 11 QPR3, Android 11 Car, obsoleto in Android 11 )
Il contenuto diActivityView
DEVE contenereFocusParkingView
come prima vista focalizzabile e il suo attributoapp:shouldRestoreFocus
DEVE esserefalse
. - Il contenuto di
ActivityView
non deve avere visualizzazioniandroid:focusByDefault
.
Per l'utente, ActivityViews non dovrebbe avere alcun effetto sulla navigazione, tranne per il fatto che le aree di interesse non possono estendersi su ActivityViews. In altre parole, non è possibile avere un'unica area di interesse con contenuto all'interno e all'esterno di ActivityView
. Se non aggiungi FocusAreas al tuo ActivityView
, la radice della gerarchia di visualizzazione in ActivityView
è considerata un'area di interesse implicita.
Pulsanti che funzionano quando vengono tenuti premuti
La maggior parte dei pulsanti provocano un'azione quando vengono cliccati. Alcuni pulsanti invece funzionano se tenuti premuti. Ad esempio, i pulsanti Avanzamento rapido e Riavvolgimento in genere funzionano quando vengono tenuti premuti. Per fare in modo che tali pulsanti supportino la rotazione, ascolta KEYCODE_DPAD_CENTER
KeyEvents
come segue:
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; });
In cui mRunnable
esegue un'azione (come il riavvolgimento) e pianifica l'esecuzione dopo un ritardo.
Modalità tocco
Gli utenti possono utilizzare un controller rotante per interagire con l'unità principale di un'auto in due modi, utilizzando il controller rotante o toccando lo schermo. Quando si utilizza la manopola di controllo, viene evidenziata una delle visualizzazioni focalizzabili. Quando si tocca lo schermo, non viene visualizzata alcuna evidenziazione della messa a fuoco. L'utente può passare da una modalità di input all'altra in qualsiasi momento:
- Rotazione → toccare. Quando l'utente tocca lo schermo, l'evidenziazione della messa a fuoco scompare.
- Toccate → rotante. Quando l'utente sposta, ruota o preme il pulsante centrale, viene visualizzata l'evidenziazione della messa a fuoco.
I pulsanti Indietro e Home non hanno alcun effetto sulla modalità di input.
Il Rotary si basa sul concetto esistente di modalità touch di Android. È possibile utilizzare View.isInTouchMode()
per determinare quale modalità di input sta utilizzando l'utente. È possibile utilizzare OnTouchModeChangeListener
per ascoltare le modifiche. Anche se questo può essere utilizzato per personalizzare l'interfaccia utente per la modalità di input corrente, evita eventuali modifiche importanti poiché possono essere sconcertanti.
Risoluzione dei problemi
In un'app progettata per il tocco, è normale avere visualizzazioni focalizzabili nidificate. Ad esempio, potrebbe esserci un FrameLayout
attorno a un ImageButton
, entrambi focalizzabili. Ciò non danneggia il tocco, ma può comportare un'esperienza utente scadente per la rotazione perché l'utente deve ruotare il controller due volte per passare alla visualizzazione interattiva successiva. Per una buona esperienza utente, Google consiglia di rendere focalizzabile la vista esterna o quella interna, ma non entrambe.
Se un pulsante o un interruttore perde la messa a fuoco quando viene premuto tramite la manopola, potrebbe verificarsi una delle seguenti condizioni:
- Il pulsante o l'interruttore è stato disabilitato (brevemente o indefinitamente) perché è stato premuto il pulsante. In entrambi i casi, ci sono due modi per risolvere questo problema:
- Lascia lo stato
android:enabled
sutrue
e utilizza uno stato personalizzato per disattivare il pulsante o passare come descritto in Custom State . - Utilizza un contenitore per circondare il pulsante o l'interruttore e rendere il contenitore attivabile anziché il pulsante o l'interruttore. (Il listener di clic deve trovarsi nel contenitore.)
- Lascia lo stato
- È in corso la sostituzione del pulsante o dell'interruttore. Ad esempio, l'azione eseguita quando si preme il pulsante o si attiva l'interruttore potrebbe attivare un aggiornamento delle azioni disponibili facendo sì che nuovi pulsanti sostituiscano quelli esistenti. Esistono due modi per risolvere questo problema:
- Invece di creare un nuovo pulsante o interruttore, imposta l'icona e/o il testo del pulsante o interruttore esistente.
- Come sopra, aggiungi un contenitore focalizzabile attorno al pulsante o all'interruttore.
RotaryPlayground
RotaryPlayground
è un'app di riferimento per il Rotary. Usalo per scoprire come integrare le funzionalità rotanti nelle tue app. RotaryPlayground
è incluso nelle build dell'emulatore e nelle build per dispositivi che eseguono il sistema operativo Android Automotive (AAOS).
- Repository
RotaryPlayground
:packages/apps/Car/tests/RotaryPlayground/
- Versioni: Android 11 QPR3, Android 11 Auto e Android 12
L'app RotaryPlayground
mostra le seguenti schede sulla sinistra:
- Carte. Prova a navigare nelle aree di interesse, saltando gli elementi non focalizzabili e l'input di testo.
- Manipolazione diretta. Testare i widget che supportano la modalità di manipolazione diretta semplice e avanzata. Questa scheda è specifica per la manipolazione diretta all'interno della finestra dell'app.
- Manipolazione dell'interfaccia utente del sistema. Testare i widget che supportano la manipolazione diretta nelle finestre di sistema in cui è supportata solo la modalità di manipolazione diretta semplice.
- Griglia. Prova la navigazione rotativa con motivo Z con scorrimento.
- Notifica. Prova a inserire e uscire dalle notifiche heads-up.
- Scorrere. Prova a scorrere un mix di contenuti focalizzabili e non focalizzabili.
- WebView. Prova la navigazione attraverso i collegamenti in un
WebView
. -
FocusArea
personalizzata. Prova la personalizzazioneFocusArea
:- Arrotolare.
-
android:focusedByDefault
eapp:defaultFocus
. - Obiettivi di spinta espliciti.
- Scorciatoie di spinta.
-
FocusArea
senza viste focalizzabili.