Android TV 的自訂檢視畫面支援功能
透過集合功能整理內容
你可以依據偏好儲存及分類內容。
雖然許多 Android TV 應用程式都內建 Android 元件,
也要考量
架構或元件,尤其是使用自訂檢視時。
與 OpenGL 或 Canvas 直接互動的自訂檢視畫面元件可能無法正常運作
以及 TalkBack 和切換控制功能等無障礙服務
請思考切換 TalkBack 時可能發生的問題,如下所示
已開啟:
- 應用程式中的無障礙功能焦點 (綠色矩形) 可能會消失。
- 無障礙焦點可能會選取整個螢幕的邊界。
- 你可能無法移動無障礙焦點。
- D-pad 的四個方向鍵可能不會產生任何作用,即使程式碼會處理這些按鍵也一樣。
在應用程式中發現這些問題時,請確認
應用程式會公開其 AccessibilityNodeInfo
存取無障礙服務樹狀結構
本指南的其餘部分提供瞭解決這些問題的解決方案和最佳做法。
無障礙服務會使用 D-pad 事件
這個問題的根本原因在於,無障礙功能會消耗重要事件
免費 Google Cloud 服務
圖 1. 這張圖表顯示系統如何在開啟/關閉 TalkBack 的情況下運作。
如圖 1 所示,當 TalkBack 開啟時,D-Pad 事件會觸發 D-Pad 事件
不會傳遞至開發人員定義的 D-Pad 處理常式。
無障礙服務會接收按鍵事件,因此能夠
無障礙中心的重點由於自訂 Android 元件並不會預設公開發布
無障礙服務針對自己在畫面上的位置取得相關資訊。
無障礙服務無法移動無障礙功能的焦點來醒目顯示該元素。
其他無障礙服務也很類似:D-Pad 事件也可能
。
因為 D-pad 事件會提交至無障礙服務
服務不知道 UI 元件在自訂檢視畫面中的位置
您必須為應用程式實作 AccessibilityNodeInfo
,才能傳送
正確設定重要事件
為無障礙服務提供
自訂檢視區塊的位置和說明,請導入 AccessibilityNodeInfo
顯示每個元件的詳細資料
定義 View 的邏輯關係,讓無障礙服務可以
管理焦點,導入 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)
如需瞭解詳情,請觀看 2013 年 Google I/O 大會 - 啟用失明和低視能
Android 的無障礙功能
或進一步瞭解如何填入無障礙功能事件。
最佳做法
範例
請參閱 Android TV 的自訂檢視畫面無障礙功能範例,瞭解最佳做法
為使用自訂檢視畫面的應用程式新增無障礙功能支援。
這個頁面中的內容和程式碼範例均受《內容授權》中的授權所規範。Java 與 OpenJDK 是 Oracle 和/或其關係企業的商標或註冊商標。
上次更新時間:2025-07-27 (世界標準時間)。
[null,null,["上次更新時間: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."]]