Hỗ trợ TalkBack trong các ứng dụng truyền hình

Ứng dụng truyền hình đôi khi có các trường hợp sử dụng gây khó khăn cho những người dùng phụ thuộc TalkBack để sử dụng ứng dụng. Để mang lại trải nghiệm TalkBack tốt hơn cho các tính năng này Hãy xem lại từng phần trong hướng dẫn này và triển khai các thay đổi trong ứng dụng của bạn khi cần thiết. Nếu ứng dụng của bạn dùng khung hiển thị tuỳ chỉnh, bạn cũng nên tham khảo hướng dẫn tương ứng mô tả cách hỗ trợ khả năng tiếp cận bằng công cụ lượt xem.

Xử lý các thành phần hiển thị lồng nhau

Chế độ xem lồng nhau có thể khiến người dùng TalkBack khó di chuyển. Bất cứ khi nào có thể, hãy đảm bảo TalkBack hoặc chế độ xem của nhà xuất bản con có thể làm tâm điểm bằng TalkBack, chứ không phải cả hai.

Để TalkBack có thể làm tâm điểm chế độ xem, hãy đặt focusable và Thuộc tính focusableInTouchMode cho true. Bước này cần thiết vì một số thiết bị TV có thể chuyển sang và thoát khỏi chế độ cảm ứng trong khi TalkBack đang hoạt động.

Để đặt một khung hiển thị không thể làm tâm điểm, bạn chỉ cần đặt focusable cho false. Tuy nhiên, nếu chế độ xem đó có thể hành động (tức là AccessibilityNodeInfo tương ứng có ACTION_CLICK), thì có thể mã này vẫn có thể có thể làm tâm điểm.

Thay đổi nội dung mô tả cho Hành động

Theo mặc định, TalkBack thông báo "Nhấn chọn để kích hoạt" cho chế độ xem có thể hành động. Đối với một số thao tác, thuật ngữ "kích hoạt" có thể không phải là nội dung mô tả hay. Người nhận cung cấp nội dung mô tả chính xác hơn, bạn có thể thay đổi nội dung đó:

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

Thêm tính năng hỗ trợ thanh trượt

TalkBack trên TV hỗ trợ đặc biệt cho thanh trượt. Để bật chế độ thanh trượt, hãy thêm ACTION_SET_PROGRESS vào một thành phần hiển thị cùng với đối tượng RangeInfo.

Người dùng chuyển sang chế độ thanh trượt bằng cách nhấn nút giữa của điều khiển từ xa của TV. Ở chế độ này, các nút mũi tên tạo ra ACTION_SCROLL_FORWARDACTION_SCROLL_BACKWARD thao tác hỗ trợ tiếp cận.

Ví dụ sau đây triển khai thanh trượt âm lượng với các mức từ 1 đến 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;
  }
}