Android TV 上的自定义视图无障碍功能支持
使用集合让一切井井有条
根据您的偏好保存内容并对其进行分类。
虽然许多 Android TV 应用都是使用原生 Android 组件构建的,
还要考虑到第三方的可访问性
框架或组件,尤其是在使用自定义视图时。
直接与 OpenGL 或画布对接的自定义视图组件可能无法正常运行
以及 TalkBack 和开关控制等无障碍服务。
请考虑切换 TalkBack 时可能遇到的以下一些问题
日期:
- 无障碍功能焦点(绿色矩形)可能会在您的应用中消失。
- 无障碍功能焦点可能会选择整个屏幕的边界。
- 无障碍功能焦点可能无法移动。
- 即使您的代码在处理方向键,方向键上的四个方向键可能也不会产生任何效果。
如果您发现应用中存在以上任一问题,请检查您的
应用公开其 AccessibilityNodeInfo
添加到无障碍服务
本指南的其余部分将针对这些问题提供一些解决方案和最佳做法。
无障碍服务使用方向键事件
此问题的根本原因在于,按键事件会被无障碍功能
服务。
图 1. 描述系统在开启和关闭 TalkBack 时如何运行的示意图。
如图 1 所示,当 TalkBack 处于开启状态时,方向键事件
不会传递给开发者定义的方向键处理程序。相反,
无障碍服务会接收按键事件
无障碍功能焦点由于自定义 Android 组件默认不会公开
向无障碍服务提供有关其在屏幕上的位置的信息;
无障碍服务无法移动无障碍功能焦点以突出显示它们。
其他无障碍服务也会受到类似影响:方向键事件
使用“开关控制”时所消耗的数据流量
由于方向键事件会提交至无障碍服务,并且
相应服务不知道界面组件位于自定义视图中的什么位置,
您必须为应用实现 AccessibilityNodeInfo
,以转发
关键事件。
为了让无障碍服务充分了解
自定义视图的位置和说明,请实现 AccessibilityNodeInfo
显示每个组件的详细信息
定义视图的逻辑关系,以便无障碍服务
管理焦点、实现 ExploreByTouchHelper
并使用
ViewCompat.setAccessibilityDelegate(View, AccessibilityDelegateCompat)
自定义视图
实现 ExploreByTouchHelper
时,替换其四个抽象方法:
Kotlin
// Return the virtual view ID whose view is covered by the input point (x, y).
protected fun getVirtualViewAt(x: Float, y: Float): Int
// Fill the virtual view ID list into the input parameter virtualViewIds.
protected fun getVisibleVirtualViews(virtualViewIds: List<Int>)
// For the view whose virtualViewId is the input virtualViewId, populate the
// accessibility node information into the AccessibilityNodeInfoCompat parameter.
protected fun onPopulateNodeForVirtualView(virtualViewId: Int, @NonNull node: AccessibilityNodeInfoCompat)
// Set the accessibility handling when perform action.
protected fun onPerformActionForVirtualView(virtualViewId: Int, action: Int, @Nullable arguments: Bundle): Boolean
Java
// Return the virtual view ID whose view is covered by the input point (x, y).
protected int getVirtualViewAt(float x, float y)
// Fill the virtual view ID list into the input parameter virtualViewIds.
protected void getVisibleVirtualViews(List<Integer> virtualViewIds)
// For the view whose virtualViewId is the input virtualViewId, populate the
// accessibility node information into the AccessibilityNodeInfoCompat parameter.
protected void onPopulateNodeForVirtualView(int virtualViewId, @NonNull AccessibilityNodeInfoCompat node)
// Set the accessibility handling when perform action.
protected boolean onPerformActionForVirtualView(int virtualViewId, int action, @Nullable Bundle arguments)
有关详情,请观看 Google I/O 2013 - 辅助盲人和弱视者观看
Android 上的无障碍功能
或详细了解如何填充无障碍事件。
最佳做法
示例
请参阅适用于 Android TV 的自定义视图无障碍功能示例,了解适用于
为使用自定义视图的应用添加无障碍支持。
本页面上的内容和代码示例受内容许可部分所述许可的限制。Java 和 OpenJDK 是 Oracle 和/或其关联公司的注册商标。
最后更新时间 (UTC):2025-07-27。
[null,null,["最后更新时间 (UTC):2025-07-27。"],[],[],null,["# Custom view accessibility support on Android TV\n\nWhile many Android TV apps are built with native Android components, it's\nalso important to consider the accessibility of third-party\nframeworks or components, especially when using [custom views](https://developer.android.com/guide/topics/ui/custom-components).\n\nCustom view components interfacing directly with OpenGL or Canvas might not work well\nwith accessibility services like Talkback and Switch Access.\n\nConsider some of the following issues that might occur with Talkback switched\non:\n\n- The accessibility focus (a green rectangle) might disappear in your app.\n- The accessibility focus might select the boundary of the whole screen.\n- The accessibility focus might not be movable.\n- The four direction keys on the D-pad might have no effect, even if your code is handling them.\n\n\u003cbr /\u003e\n\nIf you observe any of these issues in your app, check that your\napp exposes its [`AccessibilityNodeInfo`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo)\ntree to the accessibility services.\n\nThe remainder of this guide provides some solutions and best practices to address these issues.\n\nD-pad events are consumed by accessibility services\n---------------------------------------------------\n\nThe root cause of this issue is that key events are consumed by accessibility\nservices.\n\n\n**Figure 1.** Diagrams depicting how the system functions with Talkback on and off.\n\nAs illustrated in figure 1, when Talkback is switched on, D-pad events\nare not passed to the D-pad handler defined by developer. Instead,\naccessibility services receive the key events so they can move the\naccessibility focus. Because custom Android components don't by default expose\ninformation to accessibility services about their position on the screen,\naccessibility services can't move the accessibility focus to highlight them.\n\nOther accessibility services are similarly affected: D-pad events might also be\nconsumed when using Switch Access.\n\nBecause D-pad events are submitted to accessibility services, and\nthat service doesn't know where UI components are in a custom view,\nyou must implement `AccessibilityNodeInfo` for your app to forward the\nkey events correctly.\n\nExpose information to accessibility services\n--------------------------------------------\n\nTo provide accessibility services with sufficient information about the\nlocation and description of custom views, implement [`AccessibilityNodeInfo`](/reference/android/view/accessibility/AccessibilityNodeInfo)\nto expose details for each component.\nTo define the logical relationship of views so that accessibility services can\nmanage focus, implement [`ExploreByTouchHelper`](https://developer.android.com/reference/androidx/customview/widget/ExploreByTouchHelper)\nand set it using\n[`ViewCompat.setAccessibilityDelegate(View, AccessibilityDelegateCompat)`](https://developer.android.com/reference/androidx/core/view/ViewCompat#setAccessibilityDelegate(android.view.View,%20androidx.core.view.AccessibilityDelegateCompat))\nfor custom views.\n\nWhen implementing `ExploreByTouchHelper`, override its four abstract methods: \n\n### Kotlin\n\n```kotlin\n// Return the virtual view ID whose view is covered by the input point (x, y).\nprotected fun getVirtualViewAt(x: Float, y: Float): Int\n\n// Fill the virtual view ID list into the input parameter virtualViewIds.\nprotected fun getVisibleVirtualViews(virtualViewIds: List\u003cInt\u003e)\n\n// For the view whose virtualViewId is the input virtualViewId, populate the\n// accessibility node information into the AccessibilityNodeInfoCompat parameter.\nprotected fun onPopulateNodeForVirtualView(virtualViewId: Int, @NonNull node: AccessibilityNodeInfoCompat)\n\n// Set the accessibility handling when perform action.\nprotected fun onPerformActionForVirtualView(virtualViewId: Int, action: Int, @Nullable arguments: Bundle): Boolean\n```\n\n### Java\n\n```java\n// Return the virtual view ID whose view is covered by the input point (x, y).\nprotected int getVirtualViewAt(float x, float y)\n\n// Fill the virtual view ID list into the input parameter virtualViewIds.\nprotected void getVisibleVirtualViews(List\u003cInteger\u003e virtualViewIds)\n\n// For the view whose virtualViewId is the input virtualViewId, populate the\n// accessibility node information into the AccessibilityNodeInfoCompat parameter.\nprotected void onPopulateNodeForVirtualView(int virtualViewId, @NonNull AccessibilityNodeInfoCompat node)\n\n// Set the accessibility handling when perform action.\nprotected boolean onPerformActionForVirtualView(int virtualViewId, int action, @Nullable Bundle arguments)\n```\n\nFor more details, watch [Google I/O 2013 - Enabling Blind and Low-Vision\nAccessibility on Android](https://www.youtube.com/watch?v=ld7kZRpMGb8&t=1196)\nor read more about [populating accessibility events](https://developer.android.com/guide/topics/ui/accessibility/custom-views#populate-events).\n\nBest practices\n--------------\n\n- **Required:** [`AccessibilityNodeInfo.getBoundsInScreen()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#getBoundsInScreen(android.graphics.Rect))\n must define the position of the component.\n\n- **Required:** [`AccessibilityNodeInfo.setVisibleToUser()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setVisibleToUser(boolean))\n must reflect the visibility of the component.\n\n- **Required:** [`AccessibilityNodeInfo.getContentDescription()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#getContentDescription())\n must specify the content description for Talkback to announce.\n\n- Specify [`AccessibilityNodeInfo.setClassName()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setClassName(java.lang.CharSequence))\n so services can distinguish the component type.\n\n- When implementing [`performAction()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#performAction(int)),\n reflect the action using a corresponding [`AccessibilityEvent`](https://developer.android.com/reference/android/view/accessibility/AccessibilityEvent).\n\n- To implement more action types, such as `ACTION_CLICK`, invoke\n [`AccessibilityNodeInfo.addAction(ACTION_CLICK)`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#addAction(android.view.accessibility.AccessibilityNodeInfo.AccessibilityAction))\n using the corresponding logic in `performAction()`.\n\n- When applicable, reflect the component state for [`setFocusable()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setFocusable(boolean)),\n [`setClickable()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setClickable(boolean)),\n [`setScrollable()`](https://developer.android.com/reference/android/view/accessibility/AccessibilityNodeInfo#setScrollable(boolean)),\n and similar methods.\n\n- Review the documentation for [`AccessibilityNodeInfo`](/reference/android/view/accessibility/AccessibilityNodeInfo)\n to identify other ways in which accessibility services can better interact with\n your components.\n\nSample\n------\n\nConsult the [custom view accessibility sample for Android TV](/training/tv/accessibility/custom-views-sample) to see best practices for\nadding accessibility support to apps using custom views."]]