TV 앱에는 TalkBack을 사용하는 사용자가 앱을 사용하기 어려운 사용 사례가 있습니다. 이러한 사용자에게 더 나은 TalkBack 환경을 제공하려면 이 가이드의 각 섹션을 검토하고 필요한 경우 앱에서 변경사항을 구현하세요. 앱에서 맞춤 뷰를 사용하는 경우 맞춤 뷰로 접근성을 지원하는 방법을 설명하는 관련 가이드도 참고해야 합니다.
중첩된 뷰 처리
중첩된 뷰는 TalkBack 사용자가 탐색하기 어려울 수 있습니다. 가능하면 상위 뷰 또는 하위 뷰 중 하나만 TalkBack으로 포커스할 수 있도록 하고 둘 다 포커스할 수 있도록 하지 마세요.
TalkBack으로 뷰를 포커스할 수 있도록 하려면 focusable 및
focusableInTouchMode 속성을 true로 설정합니다. 일부 TV 기기는 TalkBack이 활성 상태일 때 터치 모드로 전환했다가 종료할 수 있으므로 이 단계가 필요합니다.
뷰를 포커스할 수 없도록 하려면 focusable
속성을 false로 설정하면 됩니다. 그러나 뷰가 실행 가능한 경우 (즉, 상응하는 AccessibilityNodeInfo에 ACTION_CLICK이 있음) 여전히 포커스할 수 있습니다.
작업 설명 변경
기본적으로 TalkBack은 실행 가능한 뷰에 '활성화하려면 선택을 누르세요'라고 알립니다. 일부 작업의 경우 '활성화'라는 용어가 적절한 설명이 아닐 수 있습니다. 더 정확한 설명을 제공하려면 다음과 같이 변경할 수 있습니다.
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);
}
});
슬라이더 지원 추가
TV의 TalkBack은 슬라이더를 특별히 지원합니다. 슬라이더 모드를 사용 설정하려면
ACTION_SET_PROGRESS를 RangeInfo 객체와 함께 뷰에 추가합니다.
사용자는 TV 리모컨의 가운데 버튼을 눌러 슬라이더 모드로 전환합니다.
이 모드에서 화살표 버튼은 ACTION_SCROLL_FORWARD 및
ACTION_SCROLL_BACKWARD 접근성 작업을 생성합니다.
다음 예에서는 수준이 1~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
}
}
자바
/**
* 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;
}
}