Support TalkBack in TV apps
Stay organized with collections
Save and categorize content based on your preferences.
TV apps sometimes have use cases that make it difficult for users who rely on TalkBack to use the app. To provide a better TalkBack experience for these users, review each of the sections in this guide and implement changes in your app where necessary. If your app uses custom views, you should also refer to the corresponding guide that describes how to support accessibility with custom views.
Handle nested views
Nested views can be hard for TalkBack users to navigate. Whenever possible, make either the parent or the child view focusable by TalkBack, but not both.
To make a view focusable by TalkBack, set the focusable and the
focusableInTouchMode attribute to true. This step is necessary because
some TV devices might enter and exit touch mode while TalkBack is active.
To make a view non-focusable, it is sufficient to set the focusable
attribute to false. However, if the view is actionable (that is, its
corresponding AccessibilityNodeInfo has ACTION_CLICK), it might still
be focusable.
Change descriptions for Actions
By default, TalkBack announces "Press select to activate" for actionable views. For some actions, the term "activate" might not be a good description. To provide a more accurate description, you can change it:
Kotlin
findViewById<View>(R.id.custom_actionable_view).accessibilityDelegate=object:View.AccessibilityDelegate(){ overridefunonInitializeAccessibilityNodeInfo(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(newAccessibilityDelegate(){ @Override publicvoidonInitializeAccessibilityNodeInfo(Viewhost,AccessibilityNodeInfoinfo){ super.onInitializeAccessibilityNodeInfo(host,info); AccessibilityActionaction=newAccessibilityAction( AccessibilityAction.ACTION_CLICK.getId(),getString(R.string.custom_label)); info.addAction(action); } });
Add support for sliders
TalkBack on TV has special support for sliders. To enable slider mode, add
ACTION_SET_PROGRESS to a view together with a RangeInfo object.
The user enters the slider mode by pressing the center button of the TV remote.
In this mode, the arrow buttons generate ACTION_SCROLL_FORWARD and
ACTION_SCROLL_BACKWARD accessibility actions.
The following example implements a volume slider with levels from 1 to 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. */ classVolumeSlider(context:Context?,attrs:AttributeSet?):LinearLayout(context,attrs){ privatevalmin=1 privatevalmax=10 privatevarcurrent=5 privatevartextView:TextView? =null init{ isFocusable=true isFocusableInTouchMode=true valrangeInfo= AccessibilityNodeInfo.RangeInfo( AccessibilityNodeInfo.RangeInfo.RANGE_TYPE_INT, min.toFloat(), max.toFloat(), current.toFloat() ) accessibilityDelegate= object:AccessibilityDelegate(){ overridefunonInitializeAccessibilityNodeInfo(host:View,info:AccessibilityNodeInfo){ super.onInitializeAccessibilityNodeInfo(host,info) info.addAction(AccessibilityNodeInfo.AccessibilityAction.ACTION_SET_PROGRESS) info.rangeInfo=rangeInfo } overridefunperformAccessibilityAction(host:View,action:Int,args:Bundle?):Boolean{ if(action==AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_FORWARD.id){ increase() returntrue } if(action==AccessibilityNodeInfo.AccessibilityAction.ACTION_SCROLL_BACKWARD.id){ decrease() returntrue } returnsuper.performAccessibilityAction(host,action,args) } } } overridefunonFinishInflate(){ super.onFinishInflate() textView=findViewById(R.id.text) textView!!.text=context.getString(R.string.level,current) } privatefunincrease(){ update((current+1).coerceAtMost(max)) } privatefundecrease(){ update((current-1).coerceAtLeast(min)) } privatefunupdate(newLevel:Int){ if(textView==null){ return } valnewText=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. */ publicclass VolumeSliderextendsLinearLayout{ privatefinalintmin=1; privatefinalintmax=10; privateintcurrent=5; privateTextViewtextView; publicVolumeSlider(Contextcontext,AttributeSetattrs){ super(context,attrs); setFocusable(true); setFocusableInTouchMode(true); RangeInforangeInfo=newRangeInfo(RangeInfo.RANGE_TYPE_INT,min,max,current); setAccessibilityDelegate( newAccessibilityDelegate(){ @Override publicvoidonInitializeAccessibilityNodeInfo(Viewhost,AccessibilityNodeInfoinfo){ super.onInitializeAccessibilityNodeInfo(host,info); info.addAction(AccessibilityAction.ACTION_SET_PROGRESS); info.setRangeInfo(rangeInfo); } @Override publicbooleanperformAccessibilityAction(Viewhost,intaction,Bundleargs){ if(action==AccessibilityAction.ACTION_SCROLL_FORWARD.getId()){ increase(); returntrue; } if(action==AccessibilityAction.ACTION_SCROLL_BACKWARD.getId()){ decrease(); returntrue; } returnsuper.performAccessibilityAction(host,action,args); } }); } @Override protectedvoidonFinishInflate(){ super.onFinishInflate(); textView=findViewById(R.id.text); textView.setText(getContext().getString(R.string.level,current)); } privatevoidincrease(){ update(Math.min(current+1,max)); } privatevoiddecrease(){ update(Math.max(current-1,min)); } privatevoidupdate(intnewLevel){ if(textView==null){ return; } StringnewText=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; } }