Compatibilidad con accesibilidad de vistas personalizada en Android TV

Si bien muchas apps para Android TV se compilan con componentes nativos de Android, también es importante tener en cuenta la accesibilidad de los frameworks o componentes de terceros, en especial cuando se usan vistas personalizadas.

Es posible que los componentes de vistas personalizadas que interactúan directamente con OpenGL o Canvas no funcionen bien con los servicios de accesibilidad, como TalkBack y Accesibilidad con interruptores.

Ten en cuenta algunos de los siguientes problemas que pueden ocurrir con TalkBack activado:

  • El foco de accesibilidad (un rectángulo verde) podría desaparecer en tu app.
  • El enfoque de accesibilidad puede seleccionar el límite de toda la pantalla.
  • Es posible que el enfoque de accesibilidad no se pueda mover.
  • Es posible que las cuatro teclas de dirección del pad direccional no tengan efecto, incluso si tu código las controla.

Si observas alguno de estos problemas en tu app, verifica que exponga su árbol AccessibilityNodeInfo a los servicios de accesibilidad.

En el resto de esta guía, se proporcionan algunas soluciones y prácticas recomendadas para abordar estos problemas.

Los servicios de accesibilidad consumen los eventos de pad direccional

La causa raíz de este problema es que los servicios de accesibilidad consumen los eventos de clave.

Consumo de eventos de pad direccional Figura 1: Diagramas que muestran el funcionamiento del sistema con TalkBack activado y desactivado.

Como se ilustra en la figura 1, cuando TalkBack está activado, los eventos de pad direccional no se pasan al controlador de pad direccional definido por el desarrollador. En cambio, los servicios de accesibilidad reciben los eventos clave para que puedan mover el enfoque de accesibilidad. Debido a que los componentes personalizados de Android no exponen de forma predeterminada información a los servicios de accesibilidad sobre su posición en la pantalla, estos no pueden mover el enfoque de accesibilidad para destacarlos.

Otros servicios de accesibilidad se ven afectados de manera similar: los eventos de pad direccional también pueden consumirse cuando se usa la Accesibilidad con interruptores.

Debido a que los eventos de pad direccional se envían a los servicios de accesibilidad, y ese servicio no sabe dónde se encuentran los componentes de la IU en una vista personalizada, debes implementar AccessibilityNodeInfo para que tu app reenvíe los eventos clave de forma correcta.

Exponer información a los servicios de accesibilidad

Para proporcionar servicios de accesibilidad con suficiente información sobre la ubicación y la descripción de las vistas personalizadas, implementa AccessibilityNodeInfo para exponer los detalles de cada componente. Para definir la relación lógica de las vistas de modo que los servicios de accesibilidad puedan administrar el enfoque, implementa ExploreByTouchHelper y configúralo con ViewCompat.setAccessibilityDelegate(View, AccessibilityDelegateCompat) para las vistas personalizadas.

Cuando implementes ExploreByTouchHelper, anula sus cuatro métodos abstractos:

Kotlin

// Return the virtual view ID whose view is covered by the input point (x, y).
protected fun getVirtualViewAt(x: Float, y: Float): Int

// Fill the virtual view ID list into the input parameter virtualViewIds.
protected fun getVisibleVirtualViews(virtualViewIds: List<Int>)

// For the view whose virtualViewId is the input virtualViewId, populate the
// accessibility node information into the AccessibilityNodeInfoCompat parameter.
protected fun onPopulateNodeForVirtualView(virtualViewId: Int, @NonNull node: AccessibilityNodeInfoCompat)

// Set the accessibility handling when perform action.
protected fun onPerformActionForVirtualView(virtualViewId: Int, action: Int, @Nullable arguments: Bundle): Boolean

Java

// Return the virtual view ID whose view is covered by the input point (x, y).
protected int getVirtualViewAt(float x, float y)

// Fill the virtual view ID list into the input parameter virtualViewIds.
protected void getVisibleVirtualViews(List<Integer> virtualViewIds)

// For the view whose virtualViewId is the input virtualViewId, populate the
// accessibility node information into the AccessibilityNodeInfoCompat parameter.
protected void onPopulateNodeForVirtualView(int virtualViewId, @NonNull AccessibilityNodeInfoCompat node)

// Set the accessibility handling when perform action.
protected boolean onPerformActionForVirtualView(int virtualViewId, int action, @Nullable Bundle arguments)

Para obtener más información, mira Google I/O 2013: Cómo habilitar la accesibilidad de personas ciegas y de baja visión en Android o lee más sobre cómo completar eventos de accesibilidad.

Prácticas recomendadas

Muestra

Consulta el ejemplo de accesibilidad de vistas personalizadas para Android TV a fin de ver las prácticas recomendadas para agregar compatibilidad de accesibilidad a las apps que usan vistas personalizadas.