Desarrollar aplicaciones

El siguiente material es para desarrolladores de apps.

Para que tu app admita un control rotativo, DEBES hacer lo siguiente:

  1. Coloca un elemento FocusParkingView en el diseño de la actividad correspondiente.
  2. Asegúrate de que las vistas sean (o no) enfocables.
  3. Usa objetos FocusArea para agrupar todas las vistas enfocables, excepto la FocusParkingView

Cada una de estas tareas se detalla a continuación, después de configurar tu entorno para desarrollar apps habilitadas para rotativos.

Cómo configurar un control rotativo

Antes de comenzar a desarrollar apps habilitadas para rotaciones, necesitas un control rotativo o una sustitución. Tiene las opciones que se describen a continuación.

Emulador

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

También puedes usar aosp_car_x86_64-userdebug.

Para acceder al control rotativo emulado, haz lo siguiente:

  1. Presiona los tres puntos en la parte inferior de la barra de herramientas:

    Cómo acceder al control rotativo emulado
    Figura 1: Cómo acceder al control rotativo emulado
  2. Selecciona Rotación del vehículo en la ventana de controles extendidos:

    Seleccionar control rotativo del vehículo
    Figura 2: Selecciona el control rotativo del vehículo

Teclado USB

  • Conecta un teclado USB al dispositivo que ejecute el SO Android Automotive (AAOS). En algunos casos, haz lo siguiente: De esta forma, evitas que aparezca el teclado en pantalla.
  • Usa una compilación userdebug o eng.
  • Cómo habilitar el filtrado de eventos clave:
    adb shell settings put secure android.car.ROTARY_KEY_EVENT_FILTER 1
    
  • Consulta la siguiente tabla para encontrar la clave correspondiente para cada acción:
    Clave Acción rotativa
    P Rotar en el sentido contrario a las manecillas del reloj
    E Giro en sentido horario
    A Empujar hacia la izquierda
    D Empujar hacia la derecha
    W Empujar hacia arriba
    S Empujar hacia abajo
    F o coma Centrar botón
    R o Esc Botón Atrás

Comandos de ADB

Puedes usar comandos car_service para insertar eventos de entrada rotativos. Estos comandos se puede ejecutar en dispositivos con el SO Android Automotive (AAOS) o en un emulador.

comandos de car_service Entrada rotativa
adb shell cmd car_service inject-rotary Rotar en el sentido contrario a las manecillas del reloj
adb shell cmd car_service inject-rotary -c true Giro en sentido horario
adb shell cmd car_service inject-rotary -dt 100 50 Girar a la izquierda varias veces (hace 100 ms y hace 50 ms)
adb shell cmd car_service inject-key 282 Empujar hacia la izquierda
adb shell cmd car_service inject-key 283 Empujar hacia la derecha
adb shell cmd car_service inject-key 280 Empujar hacia arriba
adb shell cmd car_service inject-key 281 Empujar hacia abajo
adb shell cmd car_service inject-key 23 Clic en el botón central
adb shell input keyevent inject-key 4 Clic en el botón Atrás

Control rotativo OEM

Cuando el hardware del control rotativo está funcionando, una opción más realista. Es particularmente útil para probar la rotación rápida.

VistaDeEstacionamientoConcentrado

FocusParkingView es una vista transparente en la Biblioteca de la IU del vehículo (car-ui-library). RotaryService lo usa para admitir la navegación del control rotativo. FocusParkingView debe ser la primera vista enfocable en el diseño. Debe colocarse fuera de todos los elementos FocusArea. Cada ventana debe tener una FocusParkingView Si ya usas el diseño base car-ui-library, que contiene un FocusParkingView, no necesitas agregar otro FocusParkingView A continuación, se muestra un ejemplo de FocusParkingView en 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>

Estos son los motivos por los que necesitas un FocusParkingView:

  1. Android no borra el enfoque automáticamente cuando el foco se establece en otra ventana. Si cuando elimines el foco de la ventana anterior, Android volverá a enfocar una vista en esa ventana, lo que da como resultado dos ventanas centradas al mismo tiempo. Agrega un FocusParkingView a cada ventana puede solucionar este problema. Esta vista es transparente y su enfoque predeterminado destaca está inhabilitado, de modo que sea invisible para el usuario, independientemente de si está enfocado o no. Puede tomar el foco para que RotaryService pueda estacionar el foco en él. para quitar el enfoque.
  2. Si solo hay un FocusArea en la ventana actual, se rotará el controlador. en FocusArea hace que RotaryService mueva el enfoque de la vista de la derecha a la de la izquierda (y viceversa). Agregando esta vista a cada ventana puede solucionar el problema. Cuando RotaryService determina el enfoque objetivo es un FocusParkingView, puede determinar que un ajuste está a punto de ocurren y evita el ajuste, ya que no mueve el foco.
  3. Cuando el control rotativo inicia una app, Android enfoca la primera vista enfocable que siempre es FocusParkingView. El FocusParkingView determina la vista óptima en la que enfocarse y, luego, aplica el enfoque.

Vistas enfocables

RotaryService se basa en el framework de Android existente de enfoque visual, que se remonta a cuando los teléfonos tenían teclados físicos y pads direccionales. El atributo android:nextFocusForward existente se reutiliza para los anuncios rotativos (consulta Personalización de FocusArea), pero android:nextFocusLeft, android:nextFocusRight, android:nextFocusUp y android:nextFocusDown no lo son.

RotaryService solo se enfoca en las vistas enfocables. Algunas vistas como los objetos Button, suelen ser enfocables. Otros, como TextView y ViewGroup, normalmente no lo son. Las vistas en las que se puede hacer clic son enfocables automáticamente y las vistas se vuelven automáticamente en el que se puede hacer clic cuando tienen un objeto de escucha de clics. Si esta lógica automática genera los resultados deseados no necesitas establecer de forma explícita la enfocabilidad de la vista. Si la lógica automática no resultado en la capacidad de enfoque deseada, establece el atributo android:focusable en true o false, o establece de manera programática la enfocabilidad de la vista con View.setFocusable(boolean) Para que RotaryService se enfoque en ella, una vista DEBE cumplan con los siguientes requisitos:

  • Enfocable
  • Habilitado
  • Visible
  • Tener valores distintos de cero para el ancho y la altura

Si una vista no cumple con todos estos requisitos (por ejemplo, un botón enfocable pero inhabilitado), el usuario no puede usar el control rotativo para enfocarse en él. Si quieres enfocarte en las vistas inhabilitadas, considera usar un estado personalizado en lugar de android:state_enabled para controlar cómo la vista aparece sin indicar que Android debe considerarla inhabilitada. Tu app puede informar al usuario por qué la vista se inhabilita cuando se la presiona. En la siguiente sección, se explica cómo hacerlo.

Estado personalizado

Para agregar un estado personalizado, sigue estos pasos:

  1. Para agregar un atributo personalizado a tu vista. Por ejemplo, para agregar un estado state_rotary_enabled personalizado a la La clase de vista CustomView, usa:
    <declare-styleable name="CustomView">
        <attr name="state_rotary_enabled" format="boolean" />
    </declare-styleable>
    
  2. Para realizar un seguimiento de este estado, agrega una variable de instancia a tu vista junto con los métodos de acceso:
    private boolean mRotaryEnabled;
    public boolean getRotaryEnabled() { return mRotaryEnabled; }
    public void setRotaryEnabled(boolean rotaryEnabled) {
        mRotaryEnabled = rotaryEnabled;
    }
    
  3. Para leer el valor del atributo cuando se crea la vista, haz lo siguiente:
    TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomView);
    mRotaryEnabled = a.getBoolean(R.styleable.CustomView_state_rotary_enabled);
    
  4. En tu clase de vistas, anula el método onCreateDrawableState() y, luego, agrega el estado personalizado, cuando corresponda. Por ejemplo:
    @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. Haz que el controlador de clics de tu vista tenga un rendimiento diferente según su estado. Por ejemplo, el es posible que el controlador de clics no haga nada o que aparezca un aviso cuando mRotaryEnabled es false.
  6. Para que el botón aparezca inhabilitado, en el elemento de diseño en segundo plano de tu vista, usa app:state_rotary_enabled en lugar de android:state_enabled. Si aún no lo tienes, deberás agregar lo siguiente:
    xmlns:app="http://schemas.android.com/apk/res-auto"
    
  7. Si la vista está inhabilitada en algún diseño, reemplaza android:enabled="false" por app:state_rotary_enabled="false" y, luego, agrega el espacio de nombres app. como se muestra arriba.
  8. Si la vista se inhabilita de manera programática, reemplaza las llamadas a setEnabled(). con llamadas a setRotaryEnabled().

Área de enfoque

Usa FocusAreas para particionar las vistas enfocables en bloques de modo que la navegación sea más fácil y coherente con otras apps. Por ejemplo, si tu app tiene una barra de herramientas, esta debe estar en un FocusArea independiente del resto de la app. Barras de pestañas y los demás elementos de navegación también deben estar separados del resto de la app. Listas grandes por lo general, deben tener su propio FocusArea. De lo contrario, los usuarios deberán rotar toda la lista para acceder a algunas vistas.

FocusArea es una subclase de LinearLayout en la biblioteca car-ui-ui. Cuando esta función está habilitada, FocusArea dibuja un resaltado cuando uno de sus elementos subordinados. Para obtener más información, consulta Personalización del enfoque destacado.

Al crear un bloque de navegación en el archivo de diseño, si deseas usar un LinearLayout como contenedor para ese bloque, usa FocusArea en su lugar. De lo contrario, une el bloque a una FocusArea.

NO anides un FocusArea en otro FocusArea. Hacerlo genera un comportamiento de navegación indefinido. Asegúrate de que todas las vistas enfocables anidada dentro de un FocusArea.

Un ejemplo de un FocusArea en RotaryPlayground se muestra a continuación:

<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 de la siguiente manera:

  1. Cuando controla las acciones de rotación y sugerencia, RotaryService busca instancias de FocusArea en la jerarquía de vistas.
  2. Cuando recibe un evento de rotación, RotaryService mueve el enfoque a otro Vista que se puede enfocar en el mismo FocusArea.
  3. Al recibir un evento de sugerencia, RotaryService mover el enfoque a otra vista que puede enfocarse en otro FocusArea (por lo general, adyacente).

Si no incluyes ningún FocusAreas en tu diseño, se tratará la vista raíz. como un área de enfoque implícita. El usuario no puede sugerirle al usuario navegar en la app. Por el contrario, roten por todas las vistas enfocables, lo que podría ser adecuado para los diálogos.

Personalización de FocusArea

Se pueden usar dos atributos de View estándares para personalizar la navegación rotativa:

  • android:nextFocusForward permite que los desarrolladores de apps especifiquen la rotación. en un área de enfoque. Este es el mismo atributo que se usa para controlar el orden de tabulación de la navegación con el teclado. NO uses este atributo para crear un bucle. En su lugar, usa app:wrapAround (consulta a continuación) para crear un bucle.
  • android:focusedByDefault permite que los desarrolladores de apps especifiquen la predeterminada en la ventana. NO uses este atributo ni app:defaultFocus (consulta a continuación) en el mismo FocusArea.

FocusArea también define algunos atributos para personalizar la navegación rotativa. Las áreas de enfoque implícitas no se pueden personalizar con estos atributos.

  1. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocus se puede usar para especificar el ID de una vista descendente enfocable, en la que se debería enfocar cuando el usuario sugerencias a esta FocusArea.
  2. (Android 11 QPR3, Android 11 Car, Android 12)
    app:defaultFocusOverridesHistory se puede establecer en true para que se enfoque la vista especificada anteriormente, incluso si para indicar otra vista de este FocusArea en la que se había enfocado.
  3. (Android 12)
    Usar app:nudgeLeftShortcut, app:nudgeRightShortcut app:nudgeUpShortcut y app:nudgeDownShortcut para especificar el ID de una vista descendente enfocable, en la que debería enfocarse cuando la el usuario hace el aporte en una dirección determinada. Para obtener más información, consulta el contenido de sugiere combinaciones de teclas a continuación.

    (Android 11 QPR3, Android 11 Car, obsoleto en Android 12) app:nudgeShortcut y app:nudgeShortcutDirection solo admitían una combinación de teclas de sugerencia.

  4. (Android 11 QPR3, Android 11 Car, Android 12)
    Para habilitar la rotación para ajustar esta FocusArea, app:wrapAround se puede establecer en true. Por lo general, se usa cuando las vistas se organizan en una óvalo o círculo.
  5. (Android 11 QPR3, Android 11 Car, Android 12)
    Para ajustar el relleno del resaltado en este FocusArea, usa app:highlightPaddingStart, app:highlightPaddingEnd, app:highlightPaddingTop app:highlightPaddingBottom, app:highlightPaddingHorizontal, y app:highlightPaddingVertical.
  6. (Android 11 QPR3, Android 11 Car, Android 12)
    Para ajustar los límites percibidos de este FocusArea a fin de encontrar un objetivo de sugerencia, usa app:startBoundOffset, app:endBoundOffset app:topBoundOffset, app:bottomBoundOffset, app:horizontalBoundOffset y app:verticalBoundOffset.
  7. (Android 11 QPR3, Android 11 Car, Android 12)
    Para especificar explícitamente el ID de un FocusArea adyacentes (o áreas) en las indicaciones dadas, utiliza app:nudgeLeft, app:nudgeRight, app:nudgeUp y app:nudgeDown Úsala cuando se use la búsqueda geométrica de forma predeterminada no encuentra el objetivo deseado.

Por lo general, los comentarios permiten navegar entre FocusAreas. Pero, con los atajos de sugerencias, Las sugerencias a veces navegan primero dentro de un FocusArea para que el usuario pueda necesitar para navegar dos veces a la siguiente FocusArea. Las combinaciones de teclas son útiles cuando un elemento FocusArea contiene una lista larga seguida de un Botón de acción flotante, como en el siguiente ejemplo:

Desplazar el acceso directo
Figura 3: Desplazar el acceso directo

Sin el atajo de sugerencia, el usuario tendría que rotar toda la lista para llegar a el FAB.

Enfocar la personalización del momento destacado

Como se indicó anteriormente, RotaryService se basa en el concepto existente de framework de Android enfoque de vista. Cuando el usuario rota y empuja, RotaryService mueve el foco, enfocar una vista y dejar de enfocar otra. En Android, cuando una vista está enfocada, si la vista:

  • Si especificó su propio punto destacado, Android lo dibuja con la vista.
  • No especifica el enfoque destacado ni el predeterminado está inhabilitado. dibuja el enfoque predeterminado para la vista.

Por lo general, las apps diseñadas para el uso táctil no especifican los aspectos destacados del enfoque adecuado.

El framework de Android proporciona el enfoque predeterminado, que se puede anular. por el OEM. Los desarrolladores de apps lo reciben cuando el tema que usan se deriva de Theme.DeviceDefault

Para ofrecer una experiencia del usuario coherente, usa el enfoque predeterminado cuando sea posible. Si necesitas un enfoque con forma personalizada (por ejemplo, redonda o con forma de píldora), o si quieres con un tema no derivado de Theme.DeviceDefault, usa car-ui-library para especificar tu propio enfoque destacado para cada vista.

Para especificar un enfoque personalizado destacado para una vista, cambia el elemento de diseño en primer o segundo plano de la vista a un elemento de diseño que difiere cuando se enfoca la vista. Por lo general, cambiarías segundo plano. El siguiente elemento de diseño, si se usa como fondo para una vista cuadrada, produce un enfoque redondo destacado:

<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) Referencias de recursos negrita en la muestra de arriba identifican los recursos definidos por la biblioteca car-ui-library. El OEM los anula para que sean coherentes. con el enfoque predeterminado que especifique. Esto garantiza que el color de resaltado del enfoque, ancho del trazo, y así sucesivamente, no cambian cuando el usuario navega entre una vista con un enfoque personalizado y una vista con el enfoque predeterminado. El último elemento es una onda utilizada para el tacto. Los valores predeterminados usados para los recursos en negrita aparecen de la siguiente manera:

Valores predeterminados para recursos en negrita
Figura 4: Valores predeterminados para recursos en negrita

Además, se llama a un enfoque personalizado destacado cuando se le da un botón sólido color de fondo para llamar la atención del usuario, como en el siguiente ejemplo. Esto puede hacer que el el enfoque destacado es difícil de ver. En este caso, especifica un enfoque destacado personalizado con secondary (secundarios):

Color de fondo sólido
  • (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 ejemplo:

Enfocado, no presionado Enfocado, presionado
Enfocado, no presionado Enfocado, presionado

Desplazamiento rotativo

Si tu app usa objetos RecyclerView, DEBERÍAS usar CarUiRecyclerView en su lugar. Esto garantiza que tu IU sea coherente con Otros porque la personalización de un OEM se aplica a todos los CarUiRecyclerView.

Si todos los elementos de tu lista son enfocables, no necesitas hacer nada más. La navegación rotativa mueve el foco entre los elementos de la lista y esta se desplaza para que el elemento enfocado recientemente sea visible.

(Android 11 QPR3, Android 11 Car, Android 12)
Si hay una combinación de enfoque y no enfocable o, si todos los elementos no son enfocables, puedes habilitar el desplazamiento rotativo, que el usuario debe usar el control rotativo para desplazarse gradualmente por la lista sin omitir elementos que no se pueden enfocar. Para habilitar el desplazamiento rotativo, configura app:rotaryScrollEnabled a true.

(Android 11 QPR3, Android 11 Car, Android 12)
Puedes habilitar el desplazamiento rotativo en cualquier vista desplazable, incluida avCarUiRecyclerView, con la setRotaryScrollEnabled() en CarUiUtils. Si lo haces, tendrás que hacer lo siguiente:

  • Haz que la vista desplazable sea enfocable para que pueda enfocarse en cuando ninguno de sus las vistas descendentes enfocables son visibles,
  • Inhabilita el enfoque predeterminado para destacar en la vista desplazable llamando a setDefaultFocusHighlightEnabled(false) para que la vista desplazable no parece estar enfocada,
  • Asegúrate de que la vista desplazable se enfoque antes de sus elementos subordinados llamando a setDescendantFocusability(ViewGroup.FOCUS_BEFORE_DESCENDANTS)
  • Escucha MotionEvents con SOURCE_ROTARY_ENCODER y cualquiera de los dos: AXIS_VSCROLL o AXIS_HSCROLL para indicar la distancia de desplazamiento y la dirección (a través del letrero).

Cuando el desplazamiento rotativo está habilitado en una CarUiRecyclerView y el usuario rota a un área en la que no hay vistas enfocables, la barra de desplazamiento cambia de gris a azul, como si para indicar que la barra de desplazamiento está enfocada. Si lo deseas, puedes implementar un efecto similar.

Los MotionEvents son los mismos que los generados por la rueda de desplazamiento del mouse. excepto por la fuente.

Modo de manipulación directa

Por lo general, las sugerencias y la rotación navegan por la interfaz de usuario mientras se presiona el botón central. tomar medidas, aunque no siempre es así. Por ejemplo, si un usuario desea ajustar el puede usar el control rotativo para navegar hasta el control deslizante del volumen, presionar Botón central, rota el control para ajustar el volumen de la alarma y presiona el botón Atrás para volver a la navegación. Esto se conoce como modo de manipulación directa (DM). En este , el control rotativo se usa para interactuar con la vista directamente en lugar de navegar.

Implementar la DM de dos maneras. Si solo necesitas controlar la rotación y la vista que deseas manipular las respuestas a ACTION_SCROLL_FORWARD y ACTION_SCROLL_BACKWARD AccessibilityEvent correctamente, usa el mecanismo simple. De lo contrario, usa el mecanismo avanzado.

El mecanismo simple es la única opción en las ventanas del sistema. las aplicaciones pueden usar cualquiera de los dos mecanismos.

Mecanismo simple

(Android 11 QPR3, Android 11 Car, Android 12)
Tu app debería llamar DirectManipulationHelper.setSupportsRotateDirectly(View view, boolean enable) RotaryService reconoce cuando el usuario está en modo de MD y entra en modo de DM cuando el usuario está en ese modo presiona el botón Centro mientras una vista está enfocada. En el modo de DM, las rotaciones realizan ACTION_SCROLL_FORWARD o ACTION_SCROLL_BACKWARD y sale del modo de MD cuando el usuario presiona el botón Atrás. El mecanismo simple activa o desactiva el estado seleccionado de la vista al ingresar y salir del modo de DM.

Para proporcionar un indicador visual de que el usuario está en modo DM, haz que tu vista se vea diferente. cuando se seleccionan. Por ejemplo, cambia el fondo cuando android:state_selected es true.

Mecanismo avanzado

La app determina en qué momento RotaryService entra y sale del modo de MD. Para obtener una experiencia experiencia del usuario, presionar el botón Centro con una vista de DM enfocada debería ingresar al modo de DM y el botón Atrás debería salir del modo DM. Si no se usan el botón Centrar o la sugerencia, pueden ser formas alternativas de salir del modo de MD. Para apps como Maps, un botón para representar Se puede usar un MD para ingresar al modo de MD.

Para admitir el modo de MD avanzado, una vista tiene las siguientes características:

  1. (Android 11 QPR3, Android 11 Car, Android 12) DEBE escuchar un KEYCODE_DPAD_CENTER. para ingresar al modo DM y escuchar un evento KEYCODE_BACK para salir del modo DM, llamando a DirectManipulationHelper.enableDirectManipulationMode() en cada caso. Para escuchar estos eventos, realiza una de las siguientes acciones:
    • Registra un OnKeyListener.
    • o
    • Extiende la vista y, luego, anula su método dispatchKeyEvent().
  2. SE RECOMIENDA escuchar los eventos de sugerencia (KEYCODE_DPAD_UP, KEYCODE_DPAD_DOWN, KEYCODE_DPAD_LEFT o KEYCODE_DPAD_RIGHT) si la vista debe administrar las sugerencias.
  3. SE DEBE escuchar los MotionEvent y obtener el recuento de rotación en AXIS_SCROLL si la vista quiere controlar la rotación. Existen varias formas de hacerlo:
    1. Registra un OnGenericMotionListener.
    2. Extiende la vista y anula su método dispatchTouchEvent().
  4. Para evitar quedar atascado en el modo DM, DEBES salir de este modo cuando el fragmento o la actividad de la vista al que pertenece no es interactivo.
  5. SE DEBE proporcionar un indicador visual para indicar que la vista está en modo de MD.

A continuación, se proporciona un ejemplo de una vista personalizada que utiliza el modo DM para desplazarse lateralmente y hacer zoom en un mapa:

/** 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(); }

Puedes encontrar más ejemplos en el RotaryPlayground proyecto.

Vista de actividad

Cuando uses un objeto ActivityView:

  • El ActivityView no debe ser enfocable.
  • (Android 11 QPR3, Android 11 Car, obsoleto en Android 11)
    El contenido de ActivityView DEBE incluir un FocusParkingView. como la primera vista enfocable y su app:shouldRestoreFocus el atributo DEBE ser false.
  • El contenido de ActivityView no debe tener android:focusByDefault vistas.

Para el usuario, ActivityViews no debería tener ningún efecto en la navegación, excepto ese enfoque. áreas no pueden abarcar ActivityViews. En otras palabras, no puedes tener una única área de enfoque tiene contenido dentro y fuera de ActivityView. Si no agregas cualquier FocusAreas a tu ActivityView, la raíz de la jerarquía de vistas en la ActivityView se considera un área de enfoque implícita.

Botones que funcionan cuando se presionan

La mayoría de los botones causan alguna acción cuando se hace clic en ellos. Algunos botones funcionan cuando se mantienen presionados. Por ejemplo, los botones de avance y retroceso suelen funcionar cuando se mantienen presionados. Para que este Los botones admiten el control rotativo; escucha KEYCODE_DPAD_CENTER KeyEvents. de la siguiente manera:

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;
});

En el que mRunnable realiza una acción (como retroceder) y se programa para ejecutarse después de una demora.

Modo táctil

Los usuarios pueden usar un control rotativo para interactuar con la consola central en un vehículo de dos maneras: usando el control rotativo o tocando la pantalla. Cuando uses el control rotativo, se destaca una de las vistas enfocables. No enfocar la pantalla cuando se toca la pantalla . El usuario puede cambiar entre estos modos de entrada en cualquier momento:

  • Rotatorio → táctil. Cuando el usuario toca la pantalla, desaparece el enfoque destacado.
  • Tocar → rotativo. Cuando el usuario empuja, rota o presiona el botón central, los aparecerá el enfoque destacado.

Los botones Atrás y Inicio no tienen efecto en el modo de entrada.

Refuerzos rotativos sobre el concepto existente de Android modo táctil. Puedes usar View.isInTouchMode() para determinar qué modo de entrada usa. Puedes usar OnTouchModeChangeListener para detectar los cambios. Si bien esto se puede usar para personalizar la interfaz de usuario modo de entrada de texto, evita los cambios importantes, ya que pueden ser desconcertantes.

Solución de problemas

En una app diseñada para el tacto, es común tener vistas enfocables anidadas. Por ejemplo, puede haber una FrameLayout alrededor de una ImageButton, ambas son enfocables. No daña el tacto, pero puede provocar una mala de la experiencia del usuario para el control rotativo porque debe rotar el control dos veces para avanzar a la siguiente vista interactiva. Para una buena experiencia del usuario, Google recomienda que hagas lo siguiente: la vista externa o interna enfocable, pero no ambas.

Si un botón o interruptor pierde el foco cuando se presiona a través del control rotativo, una de las podrían aplicarse estas condiciones:

  • El botón o interruptor se está inhabilitando (de forma breve o indefinida) debido al que se presiona el botón. En cualquier caso, hay dos formas de abordar esto:
    • Deja el estado android:enabled como true y usa un valor personalizado. para inhabilitar el botón o el interruptor, como se describe en Estado personalizado.
    • Usa un contenedor para rodear el botón o interruptor y hacer que el contenedor sea enfocable en lugar del botón o interruptor. (El objeto de escucha de clics debe estar en el contenedor).
  • Se reemplazará el botón o interruptor. Por ejemplo, la acción que se realiza cuando el botón esté presionado o que el interruptor se active, es posible que se active una actualización de las acciones disponibles lo que causa que los nuevos botones reemplacen a los existentes. Existen dos maneras de solucionar este problema:
    • En lugar de crear un nuevo botón o interruptor, establece el ícono o el texto del botón o interruptor existente.
    • Como se muestra más arriba, agrega un contenedor enfocable alrededor del botón o interruptor.

Parque de juegos rotativo

RotaryPlayground es una app de referencia para rotativos. Úsalo para aprender a integrar rotativas en tus apps. RotaryPlayground se incluye en las compilaciones del emulador y en compilaciones para dispositivos que ejecutan el SO Android Automotive (AAOS).

  • Repositorio RotaryPlayground: packages/apps/Car/tests/RotaryPlayground/
  • Versiones: Android 11 QPR3, Android 11 Car, y Android 12

La app de RotaryPlayground muestra las siguientes pestañas a la izquierda:

  • Tarjetas Prueba la navegación por las áreas de enfoque y omite los elementos no enfocables y la entrada de texto.
  • Manipulación directa. Prueba widgets que admiten funciones simples y avanzadas de manipulación directa. Esta pestaña es específicamente para la manipulación directa dentro de la ventana de la app.
  • Manipulación de la IU de sistemas. Probar widgets que admitan la manipulación directa en ventanas de sistema donde solo se admite el modo simple de manipulación directa
  • Cuadrícula. Probar la navegación rotativa del patrón z con desplazamiento
  • Notificación. Prueba sugerencias para entrar y salir de las notificaciones de atención.
  • Desplázate. Prueba el desplazamiento con una combinación de elementos enfocables y no enfocables contenido.
  • WebView. Prueba la navegación por vínculos en un WebView.
  • FocusArea personalizado. Prueba la personalización de FocusArea:
    • Ajuste.
    • android:focusedByDefault y app:defaultFocus
    • .
    • Objetivos explícitos de sugerencia.
    • Sugerir combinaciones de teclas
    • FocusArea sin vistas enfocables.