Mendukung TalkBack di aplikasi TV

Aplikasi TV terkadang memiliki kasus penggunaan yang menyulitkan pengguna yang mengandalkan TalkBack untuk menggunakan aplikasi. Untuk memberikan pengalaman TalkBack yang lebih baik bagi pengguna ini, tinjau setiap bagian dalam panduan ini dan terapkan perubahan di aplikasi Anda jika diperlukan. Jika aplikasi menggunakan tampilan kustom, Anda juga harus melihat panduan yang sesuai yang menjelaskan cara mendukung aksesibilitas dengan tampilan kustom.

Menangani tampilan bertingkat

Tampilan bertingkat mungkin sulit dibuka oleh pengguna TalkBack. Jika memungkinkan, buat tampilan induk atau turunan dapat difokuskan oleh TalkBack, tetapi tidak keduanya.

Agar tampilan dapat difokuskan oleh TalkBack, setel atribut focusable dan focusableInTouchMode ke true. Langkah ini diperlukan karena beberapa perangkat TV mungkin masuk dan keluar dari mode sentuh saat TalkBack aktif.

Agar tampilan tidak dapat difokuskan, cukup tetapkan atribut focusable ke false. Namun, jika tampilan dapat ditindaklanjuti (yaitu, AccessibilityNodeInfo yang sesuai memiliki ACTION_CLICK), tampilan mungkin masih dapat difokuskan.

Ubah deskripsi untuk Tindakan

Secara default, TalkBack mengumumkan "Tekan pilih untuk mengaktifkan" untuk tampilan yang dapat ditindaklanjuti. Untuk beberapa tindakan, istilah "aktifkan" mungkin bukan deskripsi yang baik. Untuk memberikan deskripsi yang lebih akurat, Anda dapat mengubahnya:

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

Menambahkan dukungan untuk penggeser

TalkBack di TV memiliki dukungan khusus untuk penggeser. Untuk mengaktifkan mode penggeser, tambahkan ACTION_SET_PROGRESS ke tampilan bersama dengan objek RangeInfo.

Pengguna memasuki mode penggeser dengan menekan tombol tengah remote TV. Dalam mode ini, tombol panah menghasilkan tindakan aksesibilitas ACTION_SCROLL_FORWARD dan ACTION_SCROLL_BACKWARD.

Contoh berikut menerapkan penggeser volume dengan level dari 1 hingga 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;
  }
}