O Google tem o compromisso de promover a igualdade racial para as comunidades negras. Saiba como.

Desenvolvimento de aplicativos

O material a seguir é para desenvolvedores de aplicativos.

Para tornar seu aplicativo compatível com rotativo, você DEVE:

  1. Coloque um FocusParkingView no respectivo layout de atividade.
  2. Certifique-se de que as visualizações são (ou não) focalizáveis.
  3. Use FocusArea s para envolver todos os seus pontos de vista focusable, exceto o FocusParkingView .

Cada uma dessas tarefas é detalhada a seguir, depois de configurar seu ambiente para desenvolver aplicativos habilitados para rotação.

Configure um controlador rotativo

Antes de começar a desenvolver aplicativos habilitados para rotação, você precisa de um controlador rotativo ou de um substituto. Você tem as opções 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 controlador rotativo emulado:

  1. Toque nos três pontos na parte inferior da barra de ferramentas:

    Acessar controlador rotativo emulado
    Figura 1. Acesso emulado controlador rotativa
  2. Selecione Car rotativo na janela controles estendida:

    Selecionar carro rotativo
    Figura rotativo 2. Selecione Car

Teclado USB

  • Conecte um teclado USB ao Seahawk (em alguns casos, isso pode impedir que o teclado na tela apareça).
  • Use um userdebug ou eng construção.
  • Ativar filtragem de chave evento:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • Consulte a tabela abaixo para encontrar a chave correspondente para cada ação:
    Chave Ação rotativa
    Q Girar no sentido anti-horário
    E Rode no sentido dos ponteiros do relógio
    UMA Nudge left
    D Deslocar para a direita
    C Empurrar para cima
    S Empurrar para baixo
    F ou Vírgula Botão central
    R ou Esc Botão "voltar

Comandos ADB

Você pode usar car_service comandos para injetar eventos de entrada rotativos. Esses comandos podem ser executados em um Seahawk ou emulador.

comandos car_service Entrada rotativa
adb shell cmd car_service inject-rotary Girar no sentido anti-horário
adb shell cmd car_service inject-rotary -c true Rode no sentido dos ponteiros do relógio
adb shell cmd car_service inject-rotary -dt 100 50 Girar no sentido anti-horário várias vezes (100 ms atrás e 50 ms atrás)
adb shell cmd car_service inject-key 282 Nudge left
adb shell cmd car_service inject-key 283 Deslocar para a direita
adb shell cmd car_service inject-key 280 Empurrar para cima
adb shell cmd car_service inject-key 281 Empurrar 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

Controlador rotativo OEM

Quando o hardware do seu controlador rotativo está instalado e funcionando, esta é a opção mais realista. É particularmente útil para testar rotação rápida.

FocusParkingView

FocusParkingView é uma visão transparente na UI Biblioteca Car (carro-ui-biblioteca) . RotaryService usa para apoiar a navegação controlador rotativo. FocusParkingView deve ser a primeira vista focalizável no layout. Ele deve ser colocado fora de tudo FocusArea s. Cada janela deve ter um FocusParkingView . Se você já está usando o layout base-carro-ui biblioteca, que contém uma FocusParkingView , você não precisa adicionar outro FocusParkingView . Mostrado a seguir é 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>

Aqui estão as razões que você precisa de um FocusParkingView :

  1. O Android não limpa o foco automaticamente quando o foco é definido em outra janela. Se você tentar limpar o foco na janela anterior, o Android reorientará a visualização nessa janela, o que resulta em duas janelas focadas simultaneamente. Adicionando um FocusParkingView para cada janela pode corrigir esse problema. Essa visualização é transparente e seu realce de foco padrão está desabilitado, de modo que é invisível para o usuário, independentemente de estar focado ou não. Pode demorar foco para que RotaryService pode estacionar o foco nele para remover o destaque foco.
  2. Se houver apenas uma FocusArea na janela corrente, a rotação do controlador no FocusArea provoca RotaryService para mover o foco do ponto de vista do lado direito para a vista do lado esquerdo (e vice-versa). Adicionar essa visualização a cada janela pode corrigir o problema. Quando RotaryService determina o alvo de focagem é uma FocusParkingView , ele pode determinar um wrap-around está prestes a ocorrer no ponto em que ele evita o wrap-around por não mover o foco.
  3. Quando os lançamentos de controle rotativo de um aplicativo, o Android se concentra a primeira vista focusable, que é sempre o FocusParkingView . O FocusParkingView determina a vista ideal para focar e, em seguida, aplica-se o foco.

Vistas focalizáveis

RotaryService baseia-se do quadro Android existente conceito de vista o foco, que remonta a quando os telefones tinham teclados físicos e D-pads. O existente android:nextFocusForward atributo é reaproveitado para rotativo (ver FocusArea personalização ), mas android:nextFocusLeft , android:nextFocusRight , android:nextFocusUp , e android:nextFocusDown não são.

RotaryService só incide sobre pontos de vista que são focusable. Alguns pontos de vista, como o Button s, são geralmente focusable. Outros, como TextView s e ViewGroup s, geralmente não são. As visualizações clicáveis ​​podem ser focalizadas automaticamente e as visualizações são clicáveis ​​automaticamente quando têm um ouvinte de clique. Se essa lógica automática resultar na focalização desejada, você não precisa definir explicitamente a focalização da visualização. Se a lógica automática não resultar na focusability desejado, defina o android:focusable atributo para true ou false , ou programaticamente definido focusability do ponto de vista com View.setFocusable(boolean) . Para RotaryService se concentrar nele, uma visão deve atender aos seguintes requisitos:

  • Focusable
  • Habilitado
  • Visível
  • Têm valores diferentes de zero para largura e altura

Se uma visualização não atender a todos esses requisitos, por exemplo, um botão focalizável, mas desabilitado, o usuário não pode usar o controle giratório para focalizá-lo. Se você quer foco em vista deficientes, considere usar um estado personalizado ao invés de android:state_enabled para controlar como o ponto de vista aparece sem indicando que o Android deve considerá-lo desativado. Seu aplicativo 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:

  1. Para adicionar um atributo personalizado para o seu ponto de vista. Por exemplo, para adicionar um state_rotary_enabled estado personalizado ao CustomView classe vista, use:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Para acompanhar este estado, adicione uma variável de instância à sua vista, juntamente com métodos de acesso:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. Para ler o valor de seu atributo quando sua visão é criado:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. Em sua classe vista, substituir o onCreateDrawableState() método 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;
    }
    
  5. Faça com que o manipulador de cliques de sua visualização tenha um desempenho diferente, dependendo de seu estado. Por exemplo, o manipulador de clique pode fazer nada, ou pode aparecer um brinde quando mRotaryEnabled é false .
  6. Para fazer o botão aparecer desativado, no fundo drawable do seu ponto de vista, o uso app:state_rotary_enabled vez de android:state_enabled . Se você ainda não tem, você vai precisar adicionar:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Se sua visão é desativado em quaisquer layouts, substitua android:enabled="false" com app:state_rotary_enabled="false" e, em seguida, adicionar o app namespace, como acima.
  8. Se o seu ponto de vista é programaticamente desativado, substituir chamadas para setEnabled() com chamadas para setRotaryEnabled() .

Área de foco

Use FocusAreas para particionar os pontos de vista focusable em blocos para facilitar a navegação e para ser coerente com outros aplicativos. Por exemplo, se seu aplicativo tem uma barra de ferramentas, a barra de ferramentas deve estar em separado FocusArea do resto do seu aplicativo. Barras de guias e outros elementos de navegação também devem ser separados do resto do aplicativo. Listas grandes geralmente deve ter seu próprio FocusArea . Caso contrário, os usuários devem girar por toda a lista para acessar algumas visualizações.

FocusArea é uma subclasse de LinearLayout no carro-ui-biblioteca. Quando esta função estiver ativada, uma FocusArea vai chamar um destaque quando um de seus descendentes é focado. Para saber mais, consulte Foco personalização destaque .

Ao criar um bloco de navegação no arquivo de layout, se você pretende usar um LinearLayout como um recipiente para esse bloco, use uma FocusArea vez. Caso contrário, coloque o bloco em um FocusArea .

Não um ninho FocusArea em outro FocusArea . Isso levará a um comportamento de navegação indefinido. Assegurar que todos os pontos de vista focusable são aninhados dentro de um FocusArea .

Um exemplo de um 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:

  1. Ao manusear rotação e deslocar ações, RotaryService olha para instâncias de FocusArea na hierarquia vista.
  2. Ao receber um evento de rotação, RotaryService move o foco para outra vista que pode levar foco no mesmo FocusArea .
  3. Ao receber um evento de empurrão, RotaryService foco mudança para uma outra vista que pode tomar foco no outro (normalmente adjacentes) FocusArea .

Se você não incluir qualquer FocusAreas em seu layout, a exibição de raiz é tratada como uma área de foco implícito. O usuário não pode se mover para navegar no aplicativo. Em vez disso, eles irão girar por todas as visualizações focalizáveis, o que pode ser adequado para diálogos.

Personalização FocusArea

Dois atributos de visualização padrão podem ser usados ​​para personalizar a navegação rotativa:

  • android:nextFocusForward permite que desenvolvedores de aplicativos para especificar a ordem de rotação em uma área de foco. Este é o mesmo atributo usado para controlar a ordem das guias para navegação pelo teclado. Não use este atributo para criar um loop. Em vez disso, use app:wrapAround (veja abaixo) para criar um loop.
  • android:focusedByDefault permite que desenvolvedores de aplicativos para especificar o ponto de vista foco padrão na janela. Não use este atributo e app:defaultFocus (veja abaixo) no mesmo FocusArea .

FocusArea também define alguns atributos para personalizar a navegação rotativo. As áreas de foco implícitas não podem ser personalizadas com esses atributos.

  1. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocus pode ser usado para especificar o ID de uma visão descendente focusable, que deve ser focado em quando o usuário toques a esta FocusArea .
  2. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocusOverridesHistory pode ser definido como true para fazer o ponto de vista acima especificado foco take mesmo com a história para indicar outro ponto de vista neste FocusArea tinha sido focados.
  3. (Android 12)
    Use app:nudgeLeftShortcut , app:nudgeRightShortcut , app:nudgeUpShortcut e app:nudgeDownShortcut para especificar o ID de uma visão descendente focusable, que deve ser focado em quando os toques do usuário em uma determinada direção. Para saber mais, consulte o conteúdo para atalhos nudge abaixo.

    (Android 11 QPR3, Android 11 Car, preterida no Android 12) app:nudgeShortcut e app:nudgeShortcutDirection suportado apenas um atalho cotovelada.

  4. (Android 11 QPR3, Android 11 Car, Android 12)
    Para ativar a rotação para envolver neste FocusArea , app:wrapAround pode ser definido como true . Isso é mais comumente usado quando as vistas são organizadas em um círculo ou oval.
  5. (Android 11 QPR3, Android 11 Car, Android 12)
    Para ajustar o preenchimento do destaque neste FocusArea , uso app:highlightPaddingStart , app:highlightPaddingEnd , app:highlightPaddingTop , app:highlightPaddingBottom , app:highlightPaddingHorizontal e app:highlightPaddingVertical .
  6. (Android 11 QPR3, Android 11 Car, Android 12)
    Para ajustar os limites percebidos desta FocusArea para encontrar um alvo cotovelada, o uso app:startBoundOffset , app:endBoundOffset , app:topBoundOffset , app:bottomBoundOffset , app:horizontalBoundOffset e app:verticalBoundOffset .
  7. (Android 11 QPR3, Android 11 Car, Android 12)
    Para especificar explicitamente a ID de um adjacente FocusArea (ou zonas) nas indicações dadas, uso app:nudgeLeft , app:nudgeRight , app:nudgeUp , e app:nudgeDown . Use isto quando a pesquisa geométrica usada por padrão não encontrar o alvo desejado.

Nudging geralmente navega entre FocusAreas. Mas com atalhos cotovelada, cutucando, por vezes, primeiros navega dentro de um FocusArea de modo que o usuário pode precisar deslocar duas vezes para navegar para a próxima FocusArea . Deslocar atalhos são úteis quando um FocusArea contém uma longa lista seguido por um botão de ação flutuante , como no exemplo abaixo:

Nudge atalho
Atalho Figura 3. Deslocar

Sem o atalho de deslocamento, o usuário teria que girar por toda a lista para chegar ao FAB.

Personalização de destaque de foco

Como mencionado acima, RotaryService baseia conceito existente do quadro Android de vista o foco. Quando a rotação do usuário e toques, RotaryService move a volta foco, concentrando-se um ponto de vista e desfocagem outro. No Android, quando uma visão está focada, se a visão:

  • especificou seu próprio destaque de foco, o Android desenha o destaque de foco da visualização.
  • não especifica um realce de foco e o realce de foco padrão não é desabilitado, o Android desenha o realce de foco padrão para a visualização.

Aplicativos projetados para toque geralmente não especificam os destaques de foco apropriados.

O destaque de foco padrão é fornecido pela estrutura do Android e pode ser substituído pelo OEM. Os desenvolvedores de aplicativos recebê-lo quando o tema que eles estão usando é derivado de Theme.DeviceDefault .

Para uma experiência de usuário consistente, conte com o realce de foco padrão sempre que possível. Se você precisa de um (por exemplo, redonda ou em forma de pílula) destaque foco em forma de costume, ou se você estiver usando um tema não derivado de Theme.DeviceDefault , usar o carro-ui biblioteca de recursos para especificar seu próprio destaque foco para cada vista.

Para especificar um realce de foco personalizado para uma vista, altere o drawable de fundo ou primeiro plano da vista para um drawable que difira quando a vista está focada. Normalmente, você mudaria o plano de fundo. O seguinte drawable, se usado como plano de fundo para uma vista 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 negrito no exemplo acima identificar recursos definidos pelo carro-ui-biblioteca. O OEM os substitui para serem consistentes com o realce de foco padrão que eles especificam. Isso garante que a cor de destaque do foco, a largura do traço e assim por diante não mudem quando o usuário navegar entre uma visualização com destaque de foco personalizado e uma visualização com 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:

Valores padrão para recursos em negrito
Figura 4. Os valores padrão para recursos negrito

Além disso, um destaque de foco personalizado é solicitado quando um botão recebe uma cor de fundo sólida para chamar a atenção do usuário, como no exemplo abaixo. Isso pode dificultar a visualização do destaque do foco. Nesta situação, especifique um destaque foco personalizado usando cores secundárias:

Cor de fundo sólida
  • (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

Por exemplo:

Focado, não pressionadoFocado, pressionado
Focado, não pressionado Focado, pressionado

Rolagem rotativa

Se o seu aplicativo usa RecyclerView s, você deve usar CarUiRecyclerView s em vez. Isso garante que seu UI é consistente com os outros, porque personalização de um OEM se aplica a todos CarUiRecyclerView s.

Se todos os elementos da sua lista podem ser focalizados, você não precisa fazer mais nada. A navegação rotativa move o foco pelos elementos da lista e a lista rola para tornar visível o elemento recém-focalizado.

(Android 11 QPR3, Android 11 Car, Android 12)
Se houver uma mistura de elementos focalizáveis ​​e não focalizáveis, ou se todos os elementos não forem focalizáveis, você pode ativar a rolagem giratória, que permite ao usuário usar o controlador giratório para rolar gradualmente pela lista sem pular os itens não focalizáveis. Para permitir o deslocamento rotativo, defina o app:rotaryScrollEnabled atributo a true .

(Android 11 QPR3, Android 11 Car, Android 12)
Pode permitir o deslocamento rotativo, em qualquer ponto de vista de rolagem, incluindo av CarUiRecyclerView , com o setRotaryScrollEnabled() método em CarUiUtils . Se você fizer isso, você precisa:

  • Torne a visualização rolável focalizável para que possa ser focada quando nenhuma de suas visualizações descendentes focalizáveis ​​estiverem visíveis,
  • Desativar o destaque foco padrão na exibição de rolagem chamando setDefaultFocusHighlightEnabled(false) para que a exibição de rolagem não parece ser focado,
  • Certifique-se que o ponto de vista de rolagem está focada em diante seus descendentes chamando setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS) .
  • Ouça MotionEvents com SOURCE_ROTARY_ENCODER e quer AXIS_VSCROLL ou AXIS_HSCROLL para indicar a distância a percorrer e a direção (através do sinal).

Quando rolagem rotativo é habilitado em um CarUiRecyclerView e gira o usuário para uma área onde há vistas focusable estão presentes, a barra de rolagem muda de cinza para azul, como se para indicar a barra de rolagem está focada. Você pode implementar um efeito semelhante se desejar.

Os MotionEvents são iguais aos gerados por uma roda de rolagem em um mouse, exceto para a origem.

Modo de manipulação direta

Normalmente, os toques e a rotação navegam pela interface do usuário, enquanto os pressionamentos do botão central atuam, embora nem sempre seja o caso. Por exemplo, se um usuário deseja ajustar o volume do alarme, ele pode usar o controlador giratório para navegar até o controle deslizante de volume, pressionar o botão central, girar o controlador para ajustar o volume do alarme e, em seguida, pressionar o botão Voltar para retornar à navegação . Isto é referido como o modo de manipulação directa (DM). Neste modo, o controlador rotativo é usado para interagir com a vista diretamente, em vez de navegar.

Implemente o DM de duas maneiras. Se você só precisa de rotação punho e a visão que você deseja manipular responde a ACTION_SCROLL_FORWARD e ACTION_SCROLL_BACKWARD AccessibilityEvent s apropriadamente, use o mecanismo simples. Caso contrário, utilizar o mecanismo avançado.

O mecanismo simples é a única opção nas janelas do sistema; os aplicativos podem usar qualquer um dos mecanismos.

Mecanismo simples

(Android 11 QPR3, Android 11 Car, Android 12)
Seu aplicativo deve chamar DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) . RotaryService reconhece quando o usuário está no modo de MS e entra no modo de DM quando o usuário pressiona o botão central, enquanto a vista é focada. Quando em modo de MS, rotações realizar ACTION_SCROLL_FORWARD ou ACTION_SCROLL_BACKWARD modo DM e sai quando o usuário pressiona o botão Voltar. O mecanismo simples alterna o estado selecionado da visualização ao entrar e sair do modo DM.

Para fornecer uma dica visual de que o usuário está no modo DM, faça sua visualização parecer diferente quando selecionada. Por exemplo, mudar o fundo quando android:state_selected é true .

Mecanismo avançado

O aplicativo determina quando RotaryService entra e sai modo de marcos alemães. Para uma experiência de usuário consistente, pressionar o botão central com uma visão DM focada deve entrar no modo DM e o botão Voltar deve sair do modo DM. Se o botão central e / ou nudge não forem usados, eles podem ser formas alternativas de sair do modo DM. Para aplicativos como o Maps, um botão para representar o DM pode ser usado para entrar no modo DM.

Para suportar o modo DM avançado, uma visão:

  1. (Android 11 QPR3, Android 11 Car, Android 12) deve ouvir um KEYCODE_DPAD_CENTER evento para entrar no modo de DM e ouvir um KEYCODE_BACK evento para sair do modo de DM, chamando DirectManipulationHelper.enableDirectManipulationMode() em cada caso. Para ouvir esses eventos, siga um destes procedimentos:
    • Registar um OnKeyListener .
    • ou,
    • Estender a vista e, em seguida, cancelar a sua dispatchKeyEvent() método.
  2. DEVE escutar eventos nudge ( KEYCODE_DPAD_UP , KEYCODE_DPAD_DOWN , KEYCODE_DPAD_LEFT , ou KEYCODE_DPAD_RIGHT ) se a exibição deve lidar com cotoveladas.
  3. Deve ouvir MotionEvent s e obter contagem de rotação em AXIS_SCROLL se o ponto de vista quer rotação punho. Existem várias maneiras de fazer isso:
    1. Registar um OnGenericMotionListener .
    2. Estender a vista e substituir seu dispatchTouchEvent() método.
  4. Para evitar ficar preso no modo DM, DEVE sair do modo DM quando o Fragment ou Activity ao qual a view pertence não é interativo.
  5. DEVE fornecer uma dica visual para indicar que a visualização está no modo DM.

Um exemplo de visualização personalizada que usa o modo DM para deslocar e aplicar zoom em 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 projeto.

ActivityView

Ao usar um ActivityView:

  • O ActivityView não deve ser focusable.
  • (Android 11 QPR3, Android 11 Car, preterida no Android 11)
    O conteúdo do ActivityView deve conter um FocusParkingView como a primeira vista focusable, e sua app:shouldRestoreFocus atributo deve ser false .
  • O conteúdo do ActivityView não deve ter android:focusByDefault vistas.

Para o usuário, ActivityViews não deve ter efeito na navegação, exceto que as áreas de foco não podem abranger ActivityViews. Em outras palavras, você não pode ter uma única área de foco que tem dentro de conteúdo e fora de um ActivityView . Se você não adicionar quaisquer FocusAreas ao seu ActivityView , a raiz da hierarquia de vista na ActivityView é considerada uma área de foco implícito.

Botões que operam quando pressionados

A maioria dos botões causa alguma ação quando clicados. Alguns botões funcionam quando mantidos pressionados. Por exemplo, os botões Fast Forward e Rewind normalmente operam quando pressionados. Para tornar esses botões apoiar rotativo, para ouvir 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 mRunnable toma uma acção (tais como a rebobinagem) e horários-se a ser executado depois de um atraso.

Modo de toque

Os usuários podem usar um controlador rotativo para interagir com a unidade principal em um carro de duas maneiras, usando o controlador rotativo ou tocando na tela. Ao usar o controlador giratório, uma das visualizações focalizáveis ​​será destacada. Ao tocar na tela, nenhum destaque de foco aparece. O usuário pode alternar entre esses modos de entrada a qualquer momento:

  • Rotary → toque. Quando o usuário toca a tela, o destaque do foco desaparece.
  • Toque → giratório. Quando o usuário cutuca, gira ou pressiona o botão central, o destaque do foco é exibido.

Os botões Voltar e Início não têm efeito no modo de entrada.

Sobreposto rotativo no conceito existente do Android de modo toque . Você pode usar View.isInTouchMode() para determinar qual o modo de entrada do usuário está usando. Você pode usar OnTouchModeChangeListener para escutar alterações. Embora isso possa ser usado para personalizar sua interface de usuário para o modo de entrada atual, evite quaisquer alterações importantes, pois podem ser desconcertantes.

Solução de problemas

Em um aplicativo projetado para toque, não é incomum ter visualizações focalizáveis ​​aninhadas. Por exemplo, pode haver um FrameLayout em torno de um ImageButton , ambos os quais são focalizável. Isso não prejudica o toque, mas pode resultar em uma experiência do usuário insatisfatória para o rotativo, porque o usuário deve girar o controlador duas vezes para passar para a próxima visualização interativa. Para uma boa experiência do usuário, o Google recomenda que você torne a visualização externa ou interna focalizável, mas não ambas.

Se um botão ou chave perder o foco quando pressionado por meio do controlador giratório, uma destas condições pode se aplicar:

  • O botão ou interruptor está sendo desabilitado (brevemente ou indefinidamente) devido ao botão que está sendo pressionado. Em qualquer caso, existem duas maneiras de resolver isso:
    • Deixe o android:enabled Estado como true e usar um estado personalizado a cinza para fora o botão ou interruptor, conforme descrito no estado personalizado .
    • Use um contêiner para envolver o botão ou switch e tornar o contêiner focalizável em vez do botão ou switch. (O ouvinte de clique deve estar no contêiner.)
  • O botão ou interruptor está sendo substituído. Por exemplo, a ação realizada quando o botão é pressionado ou a chave é alternada pode acionar uma atualização das ações disponíveis, fazendo com que novos botões substituam os existentes. Existem duas maneiras de resolver isso:
    • Em vez de criar um novo botão ou switch, defina o ícone e / ou texto do botão ou switch existente.
    • Como acima, adicione um contêiner focalizável ao redor do botão ou switch.

RotaryPlayground

RotaryPlayground é uma referência para aplicação rotativo. Use-o para aprender como integrar os vários recursos rotativos em seu aplicativo. RotaryPlayground está incluída no emulador e Seahawk constrói.

  • RotaryPlayground repositório: packages/apps/Car/tests/RotaryPlayground/
  • Versões: Android 11 QPR3, Android 11 Car e Android 12

Os RotaryPlayground aplicativo mostra as seguintes guias da esquerda:

  • Cartões. Teste a navegação em torno das áreas de foco, ignorando elementos não focalizáveis ​​e entrada de texto.
  • Manipulação direta. Widgets de teste que suportam o modo de manipulação direta simples e avançado. Esta guia é especificamente para manipulação direta na janela do aplicativo.
  • Manipulação de IU do sistema. Widgets de teste que suportam manipulação direta em janelas do sistema onde apenas o modo de manipulação direta simples é suportado.
  • Grade. Teste a navegação rotativa de padrão Z com rolagem.
  • Notificação. Teste a entrada e saída de notificações de alerta.
  • Rolagem. Teste a rolagem por uma mistura de conteúdo focalizável e não focalizável.
  • WebView. Teste de navegar através de links em um WebView .
  • Personalizado FocusArea . Teste FocusArea personalização:
    • Envolver em torno.
    • android:focusedByDefault e app:defaultFocus
    • .
    • Alvos de deslocamento explícitos.
    • Deslocar atalhos.
    • FocusArea sem vista focusable.