O material a seguir é para desenvolvedores de apps.
Para que seu app tenha suporte ao seletor giratório, você PRECISA:
- Coloque uma
FocusParkingView
no respectivo layout de atividade. - Garanta que as visualizações sejam (ou não) focalizáveis.
- Use
FocusArea
s para envolver todas as visualizações focalizáveis, exceto asFocusParkingView
.
Cada uma dessas tarefas está detalhada abaixo. Depois de configurar seu ambiente para desenvolver apps com seletor giratório.
Configurar um controle giratório
Antes de começar a desenvolver apps com seletor giratório, você precisa de um controle desse tipo ou um substituto. As opções estão descritas abaixo.
Emulador
source build/envsetup.sh && lunch car_x86_64-userdebug m -j emulator -wipe-data -no-snapshot -writable-system
Você também pode usar aosp_car_x86_64-userdebug
.
Para acessar o controle giratório emulado:
- Toque nos três pontos na parte de baixo da barra de ferramentas:
- Selecione Roteiro do carro na janela de controles estendidos:
Teclado USB
- Conecte um teclado USB ao dispositivo com o Android Automotive OS (AAOS). Em alguns casos, isso impede que o teclado na tela apareça.
- Use um build
userdebug
oueng
. - Ative a filtragem de eventos principais:
adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
- Consulte a tabela abaixo para encontrar a tecla correspondente a cada ação:
Chave Ação giratória Q Girar no sentido anti-horário E Girar no sentido horário A Deslocar para esquerda D Deslocar para a direita W Deslocar para cima S Deslocar para baixo F ou vírgula Botão central R ou Esc Botão "Voltar"
Comandos adb
Você pode usar comandos car_service
para injetar eventos de entrada por seletor giratório. Esses comandos
pode ser executado em dispositivos com o Android Automotive OS (AAOS) ou em um emulador.
Comandos car_service | Entrada por seletor giratório |
---|---|
adb shell cmd car_service inject-rotary |
Girar no sentido anti-horário |
adb shell cmd car_service inject-rotary -c true |
Girar no sentido horário |
adb shell cmd car_service inject-rotary -dt 100 50 |
Girar várias vezes no sentido anti-horário (100 ms atrás e 50 ms atrás) |
adb shell cmd car_service inject-key 282 |
Deslocar para esquerda |
adb shell cmd car_service inject-key 283 |
Deslocar para a direita |
adb shell cmd car_service inject-key 280 |
Deslocar para cima |
adb shell cmd car_service inject-key 281 |
Deslocar para baixo |
adb shell cmd car_service inject-key 23 |
Clique no botão central |
adb shell input keyevent inject-key 4 |
Clique no botão "Voltar" |
Controle giratório do OEM
Quando o hardware do controle giratório está em execução, essa é a configuração uma opção realista. Isso é particularmente útil para testar a rotação rápida.
FocusParkingView
FocusParkingView
é uma visualização transparente na
Biblioteca de interface do carro (car-ui-library).
RotaryService
o usa para oferecer suporte à navegação pelo controle giratório.
FocusParkingView
precisa ser a primeira visualização focalizável
no layout. Ele precisa ser colocado fora de todos os FocusArea
s. Cada janela deve ter um
FocusParkingView
: Se você já estiver usando o layout base da biblioteca da interface do carro,
que contenha um FocusParkingView
, não será preciso adicionar outro
FocusParkingView
: Veja abaixo um exemplo de FocusParkingView
em
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>
Confira por que você precisa de um FocusParkingView
:
- O Android não tira o foco automaticamente quando o foco está definido para outra janela. Se você
tentar limpar o foco na janela anterior, o Android muda o foco para uma visualização naquela janela, o que
resulta em duas janelas sendo focadas simultaneamente. Como adicionar um
FocusParkingView
a cada janela pode corrigir esse problema. Esta visualização é transparente, e o foco padrão é destacado é desativado, para que fique invisível para o usuário, independentemente de estar focado ou não. Ela pode tirar o foco para queRotaryService
possa parar o foco. para remover o destaque do foco. - Se houver apenas um
FocusArea
na janela atual, girar o controle noFocusArea
faz com que oRotaryService
mova o foco. da visualização da direita para a visualização da esquerda (e vice-versa). Como adicionar esta visualização a cada janela pode corrigir o problema. QuandoRotaryService
determina o foco for umFocusParkingView
, ele pode determinar que um encapsulamento ocorre no ponto em que ele evita a volta por não mover o foco. - Quando o controle giratório inicia um app, o Android foca a primeira visualização focalizável,
que é sempre
FocusParkingView
. OFocusParkingView
determina a visualização ideal e, em seguida, aplica o foco.
Visualizações focalizáveis
O RotaryService
se baseia na
existentes
o conceito de foco de visualização, que remonta à época em que os celulares tinham teclados físicos e botões direcionais.
O atributo android:nextFocusForward
atual foi reaproveitado para o seletor giratório
(consulte Personalização de FocusArea), mas
android:nextFocusLeft
, android:nextFocusRight
.
android:nextFocusUp
e android:nextFocusDown
não.
O RotaryService
se concentra apenas em visualizações focalizáveis. Algumas visualizações,
como Button
s,
costumam ser focalizáveis. Outros, como TextView
s e ViewGroup
s,
geralmente não são. As visualizações clicáveis são focalizáveis automaticamente, e as visualizações são automaticamente
clicáveis quando têm um listener de clique. Se essa lógica automática resultar na entrega
você não precisa definir explicitamente a capacidade de foco da visualização. Se a lógica automática não
resultem na capacidade de foco desejada, defina o atributo android:focusable
como
true
ou false
, ou definir programaticamente a capacidade de foco da visualização com
View.setFocusable(boolean)
. Para que a RotaryService
se concentre nela, uma visualização PRECISA
atendem aos seguintes requisitos:
- Focalizável
- Ativado
- Visível
- têm valores de largura e altura diferentes de zero;
Se uma visualização não atender a todos esses requisitos,
por exemplo, um botão focalizável, mas desativado,
o usuário não poderá usar
o controle giratório para focar nele. Se você quiser focar em visualizações desativadas,
use um estado personalizado em vez de android:state_enabled
para controlar a forma como
a visualização aparece sem indicar que o Android precisa considerá-la desativada. Seu app pode informar
ao usuário por que a visualização é desativada quando tocada. A próxima seção explica como fazer isso.
Estado personalizado
Para adicionar um estado personalizado:
- Para adicionar um atributo personalizado
para sua visualização. Por exemplo, para adicionar um estado personalizado
state_rotary_enabled
aoCustomView
, use:<declare-styleable name="CustomView"> <attr name="state_rotary_enabled" format="boolean" /> </declare-styleable>
- Para rastrear esse estado, adicione uma variável de instância à sua visualização junto com os métodos do acessador:
private boolean mRotaryEnabled; public boolean getRotaryEnabled() { return mRotaryEnabled; } public void setRotaryEnabled(boolean rotaryEnabled) { mRotaryEnabled = rotaryEnabled; }
- Para ler o valor do atributo quando a visualização é criada:
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView); mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
- Na classe de visualização, substitua o método
onCreateDrawableState()
e, em seguida, adicionar o estado personalizado, quando apropriado. Por exemplo:@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; }
- Faça com que o gerenciador de cliques da sua visualização tenha um desempenho diferente dependendo do estado. Por exemplo, o
o gerenciador de cliques pode não fazer nada ou pode exibir um aviso quando
mRotaryEnabled
éfalse
. - Para que o botão apareça desativado, no drawable de segundo plano da visualização, use
app:state_rotary_enabled
em vez deandroid:state_enabled
. Caso ainda não tenha, adicione:xmlns:app="http://schemas.android.com/apk/res-auto"
- Se a visualização estiver desativada em algum layout, substitua
android:enabled="false"
porapp:state_rotary_enabled="false"
e, em seguida, adicione o namespaceapp
. acima. - Se a visualização estiver desativada programaticamente, substitua as chamadas para
setEnabled()
com chamadas parasetRotaryEnabled()
.
Área de foco
Use FocusAreas
para particionar as visualizações focalizáveis em blocos e facilitar a navegação.
mais fácil e consistente com outros aplicativos. Por exemplo, se o app tiver uma barra de ferramentas, a barra de ferramentas
precisa estar em um FocusArea
separado do restante do app. Barras de guias e
outros elementos de navegação também precisam ser separados do restante do app. Listas grandes
geralmente precisam ter os próprios FocusArea
. Caso contrário, os usuários precisam alternar
a lista inteira para acessar algumas visualizações.
A FocusArea
é uma subclasse do LinearLayout
na biblioteca car-ui-library.
Quando esse recurso está ativado, o FocusArea
desenha um destaque quando um dos
em que os descendentes são focados. Para saber mais, consulte
Focar na personalização de destaques.
Ao criar um bloco de navegação no arquivo de layout, se você pretende usar um
LinearLayout
como um contêiner para esse bloco, use FocusArea
.
Caso contrário, una o bloco em uma FocusArea
.
NÃO aninhe um FocusArea
em outro FocusArea
.
Isso leva a um comportamento de navegação indefinido. Garanta que todas as visualizações focalizáveis sejam
aninhadas em uma FocusArea
.
Um exemplo de FocusArea
em
RotaryPlayground
é mostrado abaixo:
<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
funciona da seguinte maneira:
- Ao processar ações de rotação e alerta, o
RotaryService
procura instâncias. deFocusArea
na hierarquia de visualização. - Ao receber um evento de rotação,
RotaryService
move o foco para outro Visualização que pode receber foco na mesmaFocusArea
. - Ao receber um evento de alerta,
RotaryService
move o foco para outra visualização que podem ter o foco em outroFocusArea
(normalmente adjacente).
Se você não incluir FocusAreas
no layout, a visualização raiz será tratada.
como uma área de foco implícito. O usuário não consegue acionar a navegação no app. Em vez disso,
girar todas as visualizações focalizáveis, o que pode ser adequado para caixas de diálogo.
Personalização da área de foco
Dois atributos View padrão podem ser usados para personalizar a navegação por seletor giratório:
android:nextFocusForward
permite que os desenvolvedores de apps especifiquem a rotação em uma área de foco. É o mesmo atributo usado para controlar a ordem de tabulação de pelo teclado. NÃO use esse atributo para criar uma repetição. Em vez disso, useapp:wrapAround
(confira abaixo) para criar uma repetição.android:focusedByDefault
permite que os desenvolvedores de apps especifiquem o visualização de foco padrão na janela. NÃO use esse atributo eapp:defaultFocus
(confira abaixo) no mesmoFocusArea
.
O FocusArea
também define alguns atributos para personalizar a navegação por seletor giratório.
As áreas de foco implícitos não podem ser personalizadas com esses atributos.
- (Android 11 QPR3, Android 11 Car,
Android 12)
app:defaultFocus
pode ser usado para especificar o ID de uma visualização descendente focalizável, que precisa ser focada quando o usuário alertas para esteFocusArea
. - (Android 11 QPR3, Android 11 Car,
Android 12)
app:defaultFocusOverridesHistory
pode ser definido comotrue
para que a visualização especificada acima tenha foco, mesmo se com histórico para indicar que outra visualização nesteFocusArea
foi focada. - (Android 12)
Useapp:nudgeLeftShortcut
,app:nudgeRightShortcut
,app:nudgeUpShortcut
eapp:nudgeDownShortcut
para especificar o ID de uma visualização descendente focalizável, que precisa ser focada quando o o usuário alerta para uma determinada direção. Para saber mais, consulte o conteúdo de deslocar atalhos abaixo.(Android 11 QPR3, Android 11 Car, descontinuados no Android 12)
app:nudgeShortcut
eapp:nudgeShortcutDirection
ofereciam apenas um atalho de alerta. - (Android 11 QPR3, Android 11 Car,
Android 12)
Para permitir que a rotação seja agrupada nessaFocusArea
,app:wrapAround
pode ser definido comotrue
. Geralmente usado quando as visualizações são organizadas em uma círculo ou oval. - (Android 11 QPR3, Android 11 Car,
Android 12)
Para ajustar o padding do destaque em nesteFocusArea
, useapp:highlightPaddingStart
,app:highlightPaddingEnd
,app:highlightPaddingTop
,app:highlightPaddingBottom
,app:highlightPaddingHorizontal
, eapp:highlightPaddingVertical
. - (Android 11 QPR3, Android 11 Car,
Android 12)
Para ajustar os limites percebidos desseFocusArea
e encontrar um destino de alerta, usamapp:startBoundOffset
,app:endBoundOffset
,app:topBoundOffset
,app:bottomBoundOffset
,app:horizontalBoundOffset
eapp:verticalBoundOffset
. - (Android 11 QPR3, Android 11 Car,
Android 12)
Para especificar explicitamente o ID de umFocusArea
(ou áreas) adjacentes nas direções indicadas, useapp:nudgeLeft
,app:nudgeRight
,app:nudgeUp
eapp:nudgeDown
. Use quando a pesquisa geométrica usada por padrão não encontrar o destino desejado.
Os deslocamentos geralmente navegam entre FocusAreas. Mas, com os atalhos de alerta,
às vezes, o alerta navega primeiro em um FocusArea
para que o usuário precise
para acionar duas vezes e navegar para a próxima FocusArea
. Atalhos de alerta são úteis
quando uma FocusArea
contém uma lista longa seguida por uma
Botão de ação flutuante,
como no exemplo abaixo:
Sem o atalho de alerta, o usuário teria que girar por toda a lista para alcançar a FAB.
Focar a personalização de destaques
Como observado acima, RotaryService
se baseia no conceito já existente do framework do Android de
foco de visualização. Quando o usuário gira e desvia, o RotaryService
move o foco.
focando uma vista e desfocando outra. No Android, quando uma visualização for focada, se ela:
- especificar o próprio destaque de foco, o Android desenha o destaque da visualização;
- Não especifica um destaque de foco, e o destaque de foco padrão não está desativado, Android desenha o destaque de foco padrão para a visualização.
Os apps projetados para toque geralmente não especificam os destaques de foco adequados.
O destaque de foco padrão é fornecido pelo framework do Android e pode ser substituído
pelo OEM. Os desenvolvedores de apps a recebem quando o tema que estão usando é derivado
Theme.DeviceDefault
:
Para uma experiência do usuário consistente, use o destaque de foco padrão sempre que possível.
Se você precisa de um destaque com formato personalizado (por exemplo, redondo ou em forma de pílula) ou se está
usando um tema não derivado de Theme.DeviceDefault
, use a biblioteca car-ui-library
recursos para especificar seu próprio destaque de foco para cada visualização.
Para especificar um destaque de foco personalizado para uma visualização, mude o drawable de primeiro ou segundo plano da visualização para um drawable que difere quando o foco da visualização é o foco. Normalmente, você alteraria em segundo plano. O drawable a seguir, se usado como plano de fundo para uma visualização quadrada, produz um destaque de foco redondo:
<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) Referências de recursos em negrito no exemplo acima identificam os recursos definidos pela biblioteca car-ui-library. O OEM substitui essas diretrizes para manter a consistência com o destaque de foco padrão especificado. Isso garante que a cor de destaque do foco, largura do traço e assim por diante não são alterados quando o usuário navega entre uma visualização com um foco personalizado e uma visualização com o destaque de foco padrão. O último item é uma ondulação usada para toque. Os valores padrão usados para os recursos em negrito aparecem da seguinte forma:
Além disso, um destaque de foco personalizado é chamado para quando um botão recebe uma posição sólida cor de fundo para chamar a atenção do usuário, como no exemplo abaixo. Isso pode tornar o destaque do foco é difícil de ver. Nessa situação, especifique um destaque de foco personalizado usando cores secundárias:
- (Android 11 QPR3, Android 11 Car,
Android 12)
car_ui_rotary_focus_fill_secondary_color
(
)car_ui_rotary_focus_stroke_secondary_color
- (Android 12)
car_ui_rotary_focus_pressed_fill_secondary_color
(
)car_ui_rotary_focus_pressed_stroke_secondary_color
Exemplo:
Foco, não pressionado | Foco, pressionado |
Rolagem por seletor giratório
Se o app usa RecyclerView
s, você DEVE usar
CarUiRecyclerView
s. Isso garante que a interface seja consistente com
outros, porque a personalização de um OEM se aplica a todas as CarUiRecyclerView
s.
Se todos os elementos da lista puderem ser focados, você não precisará fazer mais nada. A navegação por seletor giratório move o foco pelos elementos na lista, que rola a lista para tornar visível o elemento recém-focado.
(Android 11 QPR3, Android 11 Car,
Android 12)
Se houver uma mistura de focalizável e não focal
ou, se todos os elementos não puderem ser focados, ative a rolagem por seletor giratório,
o usuário pode usar o controle giratório para rolar gradualmente pela lista sem pular
itens não focalizáveis. Para ativar a rolagem por seletor giratório, defina o app:rotaryScrollEnabled
para true
.
(Android 11 QPR3, Android 11 Car,
Android 12)
Você pode ativar a rolagem por seletor giratório em qualquer
visualização rolável, incluindo avCarUiRecyclerView
, com o
Método setRotaryScrollEnabled()
em CarUiUtils
. Se você fizer isso,
você precisa:
- Tornar a visualização rolável focalizável para que ela possa ser focada quando nenhuma das as visualizações descendentes focalizáveis são visíveis,
- Desative o destaque de foco padrão na visualização rolável chamando
setDefaultFocusHighlightEnabled(false)
para que a visualização rolável não parece estar focado, - Verifique se a visualização rolável está focada antes dos descendentes chamando
setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
: - Detectar MotionEvents com
SOURCE_ROTARY_ENCODER
eAXIS_VSCROLL
ouAXIS_HSCROLL
para indicar a distância a ser rolada e a direção (pela placa).
Quando a rolagem por seletor giratório está ativada em uma CarUiRecyclerView
e o usuário gira
para uma área em que não há visualizações focalizáveis, a barra de rolagem muda de cinza para azul, como se
para indicar que a barra de rolagem está em foco. Você pode implementar um efeito semelhante, se quiser.
Os MotionEvents são os mesmos gerados por uma roda de rolagem no mouse, com exceção da origem.
Modo de manipulação direta
Normalmente, os alertas e a rotação navegam pela interface do usuário, enquanto o pressionamento do botão central realizar ações, embora nem sempre seja o caso. Por exemplo, se um usuário quiser ajustar volume do alarme, podem usar o controle giratório para navegar até o controle deslizante de volume, pressionar o Botão central, gire o controle para ajustar o volume do alarme e depois pressione o botão "Voltar" para voltar à navegação. Isso é conhecido como modo de manipulação direta (DM). Neste o controle giratório é usado para interagir diretamente com a visualização, em vez de navegar.
Implemente as mensagens diretas de duas maneiras. Se você só precisa processar a rotação e a visualização desejada
para manipular responde a ACTION_SCROLL_FORWARD
e
ACTION_SCROLL_BACKWARD
AccessibilityEvent
s corretamente, use o
mecanismo simples. Caso contrário, use o mecanismo avançado.
O mecanismo simples é a única opção nas janelas do sistema. os apps podem usar qualquer um dos mecanismos.
Mecanismo simples
(Android 11 QPR3, Android 11 Car,
Android 12)
Seu app deve chamar
DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable)
.
RotaryService
reconhece quando o usuário está no modo de mensagem direta e entra nesse modo quando
pressiona o botão "Centro" enquanto uma visualização está em foco. No modo DM, as rotações são realizadas
ACTION_SCROLL_FORWARD
ou ACTION_SCROLL_BACKWARD
e sai do modo mensagem direta
quando o usuário pressiona o botão "Voltar". O mecanismo simples alterna o estado selecionado de
a visualização ao entrar e sair do modo DM.
Para indicar visualmente que o usuário está no modo de mensagem direta, faça com que sua visualização pareça diferente.
quando selecionado. Por exemplo, mude o plano de fundo quando
android:state_selected
é true
.
Mecanismo avançado
O app determina quando RotaryService
entra e sai do modo de mensagem direta. Para uma abordagem consistente
do usuário, pressionar o botão central com a visualização em foco vai entrar no modo de mensagens diretas
e o botão "Voltar" para sair do modo DM. Se o botão central e/ou o alerta não forem usados,
são formas alternativas de sair desse modo. Em apps como o Maps, um botão para representar
É possível usar a mensagem direta para entrar no modo de mensagem direta.
Para oferecer suporte ao modo de mensagem direta avançada, uma visualização:
- (Android 11 QPR3, Android 11 Car,
Android 12) PRECISA detectar um
KEYCODE_DPAD_CENTER
. para entrar no modo de mensagem direta e detectar um eventoKEYCODE_BACK
para sair desse modo, chamandoDirectManipulationHelper.enableDirectManipulationMode()
em cada caso. Para detectar esses eventos, siga um destes procedimentos:- Registre um
OnKeyListener
.
ou
- Estenda a visualização e modifique o método
dispatchKeyEvent()
.
- Registre um
- Você deve ouvir eventos de alerta (
KEYCODE_DPAD_UP
,KEYCODE_DPAD_DOWN
,KEYCODE_DPAD_LEFT
ouKEYCODE_DPAD_RIGHT
) se a visualização e lidar com alertas. - DEVE detectar
MotionEvent
s e receber a contagem de rotação emAXIS_SCROLL
se a visualização quiser lidar com a rotação. Há várias maneiras de fazer isso:- Registre um
OnGenericMotionListener
. - Estenda a visualização e modifique o método
dispatchTouchEvent()
.
- Registre um
- Para não ficar preso no modo de mensagem direta, PRECISA sair desse modo quando o fragmento ou a atividade abrir a visualização. não é interativo.
- DEVE fornecer uma indicação visual para indicar que a visualização está no modo DM.
Um exemplo de visualização personalizada que usa o modo DM para movimentar e aplicar zoom a um mapa é fornecido abaixo:
/** 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(); }
Mais exemplos podem ser encontrados na
RotaryPlayground
.
Visualização de atividade
Ao usar uma ActivityView:
- O
ActivityView
não precisa ser focalizável. - (Android 11 QPR3, Android 11 Car,
descontinuados no Android 11)
O conteúdo doActivityView
PRECISA conter umFocusParkingView
. como a primeira visualização focalizável, e aapp:shouldRestoreFocus
O atributo PRECISA serfalse
. - O conteúdo do
ActivityView
não pode terandroid:focusByDefault
visualizações.
Para o usuário, ActivityViews não devem afetar a navegação, exceto o foco
áreas não podem abranger ActivityViews. Em outras palavras, não é possível ter uma única área de foco
tem conteúdo dentro e fora de uma ActivityView
. Se você não adicionar
qualquer FocusAreas à ActivityView
, a raiz da hierarquia de visualização na
ActivityView
é considerada uma área de foco implícito.
Botões que funcionam quando pressionados
A maioria dos botões causa alguma ação quando clicados. Alguns botões funcionam quando pressionados.
Por exemplo, os botões "Avançar" e "Retroceder" geralmente funcionam quando pressionados. Para tornar essas
Os botões têm suporte ao seletor giratório. Detecte KEYCODE_DPAD_CENTER
KeyEvents
da seguinte forma:
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; });
em que o mRunnable
realiza uma ação (como voltar) e se programa para
ser executado após um atraso.
Modo de toque
Os usuários podem usar um controle giratório para interagir com a unidade principal em um carro de duas maneiras: usando o controle giratório ou tocando na tela. Ao usar o controle giratório, uma das visualizações focalizáveis é destacada. Sem foco ao tocar na tela aparece. O usuário pode alternar entre esses modos de entrada a qualquer momento:
- Rotativo → toque. Quando o usuário toca na tela, o destaque do foco desaparece.
- Toque → seletor giratório. Quando o usuário movimenta, gira ou pressiona o botão central, o o destaque do foco é exibido.
Os botões Voltar e Início não afetam o modo de entrada.
Embarques rotativos no conceito existente de Android
modo de toque.
Você pode usar
View.isInTouchMode()
para determinar qual modo de entrada o usuário está usando. Você pode usar
OnTouchModeChangeListener
para detectar alterações. Embora isso possa ser usado para personalizar a interface do usuário para o
modo de entrada, evite grandes alterações, pois elas podem ser desconcertantes.
Solução de problemas
Em um app projetado para toque, é comum ter visualizações focalizáveis aninhadas.
Por exemplo, pode haver um FrameLayout
em torno de um ImageButton
,
sendo que ambos são focalizáveis. Isso não prejudica o toque, mas pode resultar em uma
experiência do usuário para o seletor giratório porque o usuário precisa girar o controle duas vezes para mover para
a próxima visualização interativa. Para uma boa experiência do usuário, o Google recomenda que você faça
a visualização externa ou a interna são focalizáveis, mas não ambas.
Se um botão ou interruptor perder o foco quando pressionado no controle giratório, um dos estas condições podem ser aplicáveis:
- O botão ou interruptor está sendo desativado (brevemente ou indefinidamente) devido à
o botão sendo pressionado. Em ambos os casos, há duas maneiras de resolver isso:
- Deixe o estado
android:enabled
comotrue
e use um para esmaecer o botão ou o botão, conforme descrito em Estado personalizado. - Use um contêiner para cercar o botão ou interruptor e torná-lo focalizável em vez de um botão ou um interruptor. O listener de clique precisa estar no contêiner.
- Deixe o estado
- O botão ou interruptor está sendo substituído. Por exemplo, a ação realizada quando o botão
for pressionada ou se o interruptor estiver alternado, as ações disponíveis serão atualizadas
fazendo com que os novos botões substituam os existentes. Há duas maneiras de lidar com isso:
- Em vez de criar um novo botão ou interruptor, defina o ícone e/ou o texto do botão ou interruptor existente.
- Como acima, adicione um contêiner focalizável ao redor do botão ou interruptor.
RotaryPlayground
O RotaryPlayground
é um app de referência para o seletor giratório. Use para aprender a integrar
os recursos giratórios nos seus apps. RotaryPlayground
está incluído em builds do emulador e nos
para dispositivos com o Android Automotive OS (AAOS).
- Repositório
RotaryPlayground
:packages/apps/Car/tests/RotaryPlayground/
- Versões: Android 11 QPR3, Android 11 Car, e o Android 12
O app RotaryPlayground
mostra as seguintes guias à esquerda:
- Cards. Testar a navegação pelas áreas de foco, pulando elementos que não podem ser focados e entrada de texto.
- Manipulação direta. Testar widgets que oferecem suporte a widgets simples e avançados o modo de manipulação direta. Essa guia serve especificamente para manipulação direta do janela do app.
- Manipulação de interface do Sys. Testar widgets que oferecem suporte à manipulação direta em janelas do sistema em que apenas o modo de manipulação direta simples é suportado.
- Grade. Testar a navegação por seletor giratório do padrão z com rolagem.
- Notificação. Tente ativar e desativar as notificações de alerta.
- Role. Teste a rolagem por uma combinação de focalizável e não focalizável conteúdo.
- WebView. Teste a navegação por links em um
WebView
. FocusArea
personalizado. Teste a personalização deFocusArea
:- Encerramento.
android:focusedByDefault
eapp:defaultFocus
.
- Destinos de alertas explícitos.
- Deslocar atalhos.
FocusArea
sem visualizações focalizáveis.