Поддержка TalkBack в телевизионных приложениях

ТВ-приложения иногда имеют сценарии использования, которые затрудняют использование приложения пользователями, которые полагаются на TalkBack . Чтобы улучшить работу TalkBack для этих пользователей, просмотрите каждый раздел этого руководства и при необходимости внесите изменения в свое приложение. Если ваше приложение использует настраиваемые представления, вам также следует обратиться к соответствующему руководству , в котором описывается, как поддерживать специальные возможности с помощью настраиваемых представлений.

Обработка вложенных представлений

Пользователям TalkBack может быть сложно ориентироваться во вложенных представлениях. По возможности делайте так, чтобы TalkBack фокусировал либо родительское, либо дочернее представление, но не оба одновременно.

Чтобы TalkBack мог фокусировать представление, установите для атрибута focusable и focusableInTouchMode значение true . Этот шаг необходим, поскольку некоторые телевизионные устройства могут переходить в сенсорный режим и выходить из него, когда TalkBack активен.

Чтобы сделать представление нефокусируемым, достаточно установить для атрибута focusable значение false . Однако если представление является активным (то есть соответствующий ему AccessibilityNodeInfo имеет ACTION_CLICK ), оно все равно может быть фокусируемым.

Изменить описания действий

По умолчанию TalkBack объявляет: «Нажмите «Выбрать, чтобы активировать»» для интерактивных представлений. Для некоторых действий термин «активировать» может оказаться неподходящим описанием. Чтобы дать более точное описание, вы можете его изменить:

Котлин

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

Ява

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

Добавить поддержку слайдеров

TalkBack на ТВ имеет специальную поддержку ползунков. Чтобы включить режим ползунка, добавьте ACTION_SET_PROGRESS в представление вместе с объектом RangeInfo .

Пользователь входит в режим слайдера, нажав центральную кнопку пульта телевизора. В этом режиме кнопки со стрелками генерируют действия доступности ACTION_SCROLL_FORWARD и ACTION_SCROLL_BACKWARD .

В следующем примере реализован ползунок громкости с уровнями от 1 до 10:

Котлин

/**
 *   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
  }
}

Ява

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