A veces, las apps para TV tienen casos de uso que dificultan el uso de la app para los usuarios que dependen de TalkBack. Para brindarles una mejor experiencia con TalkBack, revisa cada una de las secciones de esta guía y, luego, implementa cambios en tu app cuando sea necesario. Si tu app usa vistas personalizadas, también debes consultar la guía correspondiente en la que se describe cómo admitir la accesibilidad con vistas personalizadas.
Cómo controlar vistas anidadas
Los usuarios de TalkBack pueden tener dificultades para navegar por las vistas anidadas. Siempre que sea posible, haz que TalkBack pueda enfocar la vista principal o la secundaria, pero no ambas.
Para que TalkBack pueda enfocar una vista, establece los atributos focusable
y focusableInTouchMode
en true
. Este paso es necesario porque algunos dispositivos de TV pueden entrar y salir del modo táctil mientras TalkBack está activo.
Para que una vista no se pueda enfocar, es suficiente establecer el atributo focusable
en false
. Sin embargo, si la vista es accionable (es decir, su AccessibilityNodeInfo
correspondiente tiene ACTION_CLICK
), es posible que aún sea enfocable.
Descripciones de los cambios de las acciones
De forma predeterminada, TalkBack anuncia "Presiona seleccionar para activar" para las vistas que se pueden tomar acciones. En el caso de algunas acciones, es posible que el término "activar" no sea una buena descripción. Para proporcionar una descripción más precisa, puedes cambiarla de la siguiente manera:
Kotlin
findViewById<View>(R.id.custom_actionable_view).accessibilityDelegate = object : View.AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) { super.onInitializeAccessibilityNodeInfo(host, info) info.addAction( AccessibilityAction( AccessibilityAction.ACTION_CLICK.id, getString(R.string.custom_label) ) ) } }
Java
findViewById(R.id.custom_actionable_view).setAccessibilityDelegate(new AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); AccessibilityAction action = new AccessibilityAction( AccessibilityAction.ACTION_CLICK.getId(), getString(R.string.custom_label)); info.addAction(action); } });
Cómo agregar compatibilidad con controles deslizantes
TalkBack en TV tiene compatibilidad especial para los controles deslizantes. Para habilitar el modo deslizante, agrega ACTION_SET_PROGRESS
a una vista junto con un objeto RangeInfo
.
El usuario ingresa al modo deslizante presionando el botón central del control remoto de la TV.
En este modo, los botones de flecha generan acciones de accesibilidad ACTION_SCROLL_FORWARD
y ACTION_SCROLL_BACKWARD
.
En el siguiente ejemplo, se implementa un control deslizante de volumen con niveles del 1 al 10:
Kotlin
/** * This example only provides slider functionality for TalkBack users. Additional logic should * be added for other users. Such logic may reuse the increase and decrease methods. */ class VolumeSlider(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) { private val min = 1 private val max = 10 private var current = 5 private var textView: TextView? = null init { isFocusable = true isFocusableInTouchMode = true val rangeInfo = AccessibilityNodeInfo.RangeInfo( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, min.toFloat(), max.toFloat(), current.toFloat() ) accessibilityDelegate = object : AccessibilityDelegate() { override fun onInitializeAccessibilityNodeInfo(host: View, info: AccessibilityNodeInfo) { super.onInitializeAccessibilityNodeInfo(host, info) info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS) info.rangeInfo = rangeInfo } override fun performAccessibilityAction(host: View, action: Int, args: Bundle?): Boolean { if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.id) { increase() return true } if (action == AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD.id) { decrease() return true } return super.performAccessibilityAction(host, action, args) } } } override fun onFinishInflate() { super.onFinishInflate() textView = findViewById(R.id.text) textView!!.text = context.getString(R.string.level, current) } private fun increase() { update((current + 1).coerceAtMost(max)) } private fun decrease() { update((current - 1).coerceAtLeast(min)) } private fun update(newLevel: Int) { if (textView == null) { return } val newText = context.getString(R.string.level, newLevel) // Update the user interface with the new volume. textView!!.text = newText // Announce the new volume. announceForAccessibility(newText) current = newLevel } }
Java
/** * This example only provides slider functionality for TalkBack users. Additional logic should * be added for other users. Such logic can reuse the increase and decrease methods. */ public class VolumeSlider extends LinearLayout { private final int min = 1; private final int max = 10; private int current = 5; private TextView textView; public VolumeSlider(Context context, AttributeSet attrs) { super(context, attrs); setFocusable(true); setFocusableInTouchMode(true); RangeInfo rangeInfo = new RangeInfo(RangeInfo.RANGE_TYPE_INT, min, max, current); setAccessibilityDelegate( new AccessibilityDelegate() { @Override public void onInitializeAccessibilityNodeInfo(View host, AccessibilityNodeInfo info) { super.onInitializeAccessibilityNodeInfo(host, info); info.addAction(AccessibilityAction.ACTION_SET_PROGRESS); info.setRangeInfo(rangeInfo); } @Override public boolean performAccessibilityAction(View host, int action, Bundle args) { if (action == AccessibilityAction.ACTION_SCROLL_FORWARD.getId()) { increase(); return true; } if (action == AccessibilityAction.ACTION_SCROLL_BACKWARD.getId()) { decrease(); return true; } return super.performAccessibilityAction(host, action, args); } }); } @Override protected void onFinishInflate() { super.onFinishInflate(); textView = findViewById(R.id.text); textView.setText(getContext().getString(R.string.level, current)); } private void increase() { update(Math.min(current + 1, max)); } private void decrease() { update(Math.max(current - 1, min)); } private void update(int newLevel) { if (textView == null) { return; } String newText = getContext().getString(R.string.level, newLevel); // Update the user interface with the new volume. textView.setText(newText); // Announce the new volume. announceForAccessibility(newText); current = newLevel; } }