• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# 通过XComponent接入无障碍
2<!--Kit: ArkUI-->
3<!--Subsystem: ArkUI-->
4<!--Owner: @zhanghangkai10241-->
5<!--Designer: @lmleon-->
6<!--Tester: @fredyuan0912-->
7<!--Adviser: @HelloCrease-->
8
9通过XComponent接入的三方平台,NDK提供了对接无障碍的接口函数,实现三方平台的组件在ArkUI中的无障碍能力。
10
11首先,需要使用XComponent的[OH_NativeXComponent_GetNativeAccessibilityProvider](../reference/apis-arkui/capi-native-interface-xcomponent-h.md#oh_nativexcomponent_getnativeaccessibilityprovider)获得无障碍接入provider。然后,通过[OH_ArkUI_AccessibilityProviderRegisterCallback](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_accessibilityproviderregistercallback)注册接入无障碍所需的回调函数[ArkUI_AccessibilityProviderCallbacks](../reference/apis-arkui/capi-arkui-accessibility-arkui-accessibilityprovidercallbacks.md),三方应用需要按照接口要求实现回调函数供无障碍系统调用。
12
13三方应用需要按照要求适配无障碍系统发出的操作[Action](../reference/apis-arkui/capi-native-interface-accessibility-h.md#arkui_accessibility_actiontype),以及针对组件交互行为发送无障碍事件[Event](../reference/apis-arkui/capi-native-interface-accessibility-h.md#arkui_accessibilityeventtype)到无障碍子系统,实现无障碍辅助应用的交互体验。
14
15> **说明:**
16>
17> - 无障碍能力:指开发者能够创建可访问的应用界面,满足视觉、听觉、运动和认知障碍等用户需求的能力。
18> - 实现[OH_ArkUI_AccessibilityProviderRegisterCallback](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_accessibilityproviderregistercallback)回调查询接口时,查询到的每个无障碍节点信息通过[OH_ArkUI_AddAndGetAccessibilityElementInfo](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_addandgetaccessibilityelementinfo)创建分配element内存,并将其加入到指定的elementList中。
19> - 使用[OH_ArkUI_SendAccessibilityAsyncEvent](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_sendaccessibilityasyncevent)发送事件时,需要使用[OH_ArkUI_CreateAccessibilityEventInfo](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_createaccessibilityeventinfo)创建[ArkUI_AccessibilityEventInfo](../reference/apis-arkui/capi-arkui-accessibility-arkui-accessibilityeventinfo.md),使用[OH_ArkUI_CreateAccessibilityElementInfo](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_createaccessibilityelementinfo)创建[ArkUI_AccessibilityElementInfo](../reference/apis-arkui/capi-arkui-accessibility-arkui-accessibilityelementinfo.md),使用结束后,需要调用[OH_ArkUI_DestoryAccessibilityEventInfo](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_destoryaccessibilityeventinfo)以及[OH_ArkUI_DestoryAccessibilityElementInfo](../reference/apis-arkui/capi-native-interface-accessibility-h.md#oh_arkui_destoryaccessibilityelementinfo)销毁函数释放内存。
20> - 回调函数打印日志时,携带输入的requestId,用于关联一次交互过程相关的日志,便于索引查询整个流程,协助问题定位。
21
22## 对接无障碍
23
24以下示例提供了对接无障碍能力的实现方法。对接完成后,在开启无障碍功能时,可使XComponent中的三方绘制组件接入,实现无障碍交互。
25
261.按照自定义渲染(XComponent)的[使用OH_ArkUI_SurfaceHolder管理Surface生命周期](napi-xcomponent-guidelines.md#使用oh_arkui_surfaceholder管理surface生命周期)场景创建前置工程。
27
282.根据接口定义实现回调函数。
29
30```c
31int32_t FindAccessibilityNodeInfosById(int64_t elementId, ArkUI_AccessibilitySearchMode mode, int32_t requestId, ArkUI_AccessibilityElementInfoList* elementList)
32{
33    // 根据mode搜集查询element信息列表
34	if (elementList == nullptr) {
35        return OH_NATIVEXCOMPONENT_RESULT_FAILED;
36    }
37
38    // 调用三方平台的接口搜索查询获得mode符合条件的节点
39    //...
40    // nodes为查询节点结果
41    int size = sizeof(nodes) / sizeof(nodes[0]);
42    for (int i = 0; i < size; i++) {
43        // 获取element结构
44        element = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);
45        // 设置element成员内容
46        OH_ArkUI_AccessibilityElementInfoSetElementId(element, nodes[i].id);
47        OH_ArkUI_AccessibilityElementInfoSetComponentType(element, nodes[i].type);
48        // ...
49    }
50	// ...
51}
52```
53
54
55
56```c
57int32_t FindNextFocusAccessibilityNode(int64_t elementId, ArkUI_AccessibilityFocusType focusType, int32_t requestId, ArkUI_AccessibilityElementInfo* elementinfo)
58{
59    // 根据mode搜集查询element信息列表,参考接口说明实现
60	if (elementinfo == nullptr) {
61        return OH_NATIVEXCOMPONENT_RESULT_FAILED;
62    }
63
64    // 调用三方平台自身的接口搜索查询获得符合条件的节点
65    //...
66    // node为查询节点结果
67    // 设置element成员内容
68    OH_ArkUI_AccessibilityElementInfoSetElementId(element, nodes[i].id);
69    OH_ArkUI_AccessibilityElementInfoSetComponentType(element, nodes[i].type);
70    // ...
71}
72```
73
74
75
76```c
77int32_t FindAccessibilityNodeInfosByText(int64_t elementId, const char *text, int32_t requestId, ArkUI_AccessibilityElementInfoList* elementList)
78{
79    if (elementList == nullptr) {
80        return OH_NATIVEXCOMPONENT_RESULT_FAILED;
81    }
82
83    ArkUI_AccessibilityElementInfo *elementInfo = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);
84
85    // 需要三方平台自身设置elementInfo组件属性
86
87    if (elementInfo == nullptr) {
88        return OH_NATIVEXCOMPONENT_RESULT_FAILED;
89    }
90    return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
91}
92```
93
94
95
96```c
97int32_t FindFocusedAccessibilityNode(int64_t elementId, ArkUI_AccessibilityFocusType focusType, int32_t requestId, ArkUI_AccessibilityElementInfo *elementInfo)
98{
99    if (elementInfo == nullptr) {
100        return OH_NATIVEXCOMPONENT_RESULT_FAILED;
101    }
102
103    // 需要三方平台自身设置elementInfo组件属性
104
105    return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;
106}
107```
108
109
110
111```C
112int32_t ExecuteAccessibilityAction(int64_t elementId, ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments *actionArguments, int32_t requestId)
113{
114    // ...
115    // 获取action argument内容,结合action判断当前需要进行的操作处理
116    char* actionArgumentValue;
117    OH_ArkUI_FindAccessibilityActionArgumentByKey(actionArguments, key.c_str(), &actionArgumentValue);
118
119    // 针对指定组件节点进行操作
120    ret = doAction(elementId, action, actionArgumentValue);
121    if (ret != 0) {
122        return;
123    }
124    // 判断当前处理操作类型,返回对应的event结果。每个不同的操作有对应不同的event。参考:ArkUI_AccessibilityEventType定义
125    // ...
126    // 明确当前上报事件的组件节点为node
127    // 1.调用OH_ArkUI_CreateAccessibilityEventInfo创建ArkUI_AccessibilityEventInfo
128    ArkUI_AccessibilityEventInfo *eventInfo = OH_ArkUI_CreateAccessibilityEventInfo();
129    if (eventInfo == nullptr) {
130        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "[requestId: %{public}d]DispatchTouchEventCB: Unable to create accessibility eventInfo", requestId);
131        return;
132    }
133    // 2.调用OH_ArkUI_CreateAccessibilityElementInfo创建ArkUI_AccessibilityElementInfo
134    ArkUI_AccessibilityElementInfo *elementInfo = OH_ArkUI_CreateAccessibilityElementInfo();
135    if (elementInfo == nullptr) {
136        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "[requestId: %{public}d]DispatchTouchEventCB: Unable to create accessibility elementInfo", requestId);
137        return;
138    }
139    // 3.填写element内容
140    // 设置element成员内容
141    OH_ArkUI_AccessibilityElementInfoSetElementId(element, nodes[i].id);
142    OH_ArkUI_AccessibilityElementInfoSetComponentType(element, nodes[i].type);
143
144    OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "[requestId: %{public}d]DispatchTouchEventCB: send accessibility event", requestId);
145    // 4.eventType根据当前Action设置
146    // ...
147    SendAccessibilityAsyncEvent(eventInfo, elementInfo, eventType);
148    // 5.销毁eventInfo,elementInfo内存
149    OH_ArkUI_DestoryAccessibilityElementInfo(elementInfo);
150    OH_ArkUI_DestoryAccessibilityEventInfo(eventInfo);
151    // ...
152}
153void FillEvent(ArkUI_AccessibilityEventInfo *eventInfo, ArkUI_AccessibilityElementInfo *elementInfo, ArkUI_AccessibilityEventType eventType)
154{
155    if (eventInfo == nullptr) {
156        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_TEXT, "eventInfo is null");
157        return;
158    }
159    if (elementInfo == nullptr) {
160        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_TEXT, "elementInfo is null");
161        return;
162    }
163    OH_ArkUI_AccessibilityEventSetEventType(eventInfo, eventType);
164
165    OH_ArkUI_AccessibilityEventSetElementInfo(eventInfo, elementInfo);
166
167}
168void SendAccessibilityAsyncEvent(ArkUI_AccessibilityEventInfo *eventInfo, ArkUI_AccessibilityElementInfo *elementInfo, ArkUI_AccessibilityEventType eventType)
169{
170    // 1.填写event内容
171    FillEvent(eventInfo, elementInfo, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_STATE_UPDATE);
172    // 2.callback
173    auto callback = [](int32_t errorCode){
174         OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "result: %{public}d", errorCode);
175    };
176    // 3. 调用接口发送事件给OH侧
177    OH_ArkUI_SendAccessibilityAsyncEvent(provider_, eventInfo, callback);
178}
179```
180
181
182
183```C
184int32_t ClearFocusedFocusAccessibilityNode()
185{
186	// 找到当前获焦的组件,并清除焦点状态。
187    // ...
188    // 无障碍焦点状态
189    node.accessibilityFocused = false;
190    // 组件焦点状态
191    node.focused = false;
192    // ...
193}
194```
195
196
197
198```C
199int32_t GetAccessibilityNodeCursorPosition(int64_t elementId, int32_t requestId, int32_t* index)
200{
201	// 获取文本组件光标位置,并返回
202    // 查找对应组件节点node
203    // ...
204    *index = node.cursorPosition;
205    // ...
206}
207```
208
209
210
2113.使用XComponent句柄注册无障碍回调函数。
212
213```C
214void PluginRender::RegisterAccessibility(OH_NativeXComponent* nativeXComponent)
215{
216	//...
217    //1.获取provider实例,定义”provider_“提供给函数返回
218    int32_t ret = OH_NativeXComponent_GetNativeAccessibilityProvider(nativeXComponent, &provider_);
219    if (provider_ == nullptr) {
220        return;
221    }
222    //2.注册回调函数,如下相关回调注册函数FindAccessibilityNodeInfosById等,需三方实现函数注册。
223    accessibilityProviderCallbacks_ = new ArkUI_AccessibilityProviderCallbacks();
224    accessibilityProviderCallbacks_->findAccessibilityNodeInfosById = FindAccessibilityNodeInfosById;
225    accessibilityProviderCallbacks_->findAccessibilityNodeInfosByText = FindAccessibilityNodeInfosByText;
226    accessibilityProviderCallbacks_->findFocusedAccessibilityNode = FindFocusedAccessibilityNode;
227    accessibilityProviderCallbacks_->findNextFocusAccessibilityNode = FindNextFocusAccessibilityNode;
228    accessibilityProviderCallbacks_->executeAccessibilityAction = ExecuteAccessibilityAction;
229    accessibilityProviderCallbacks_->clearFocusedFocusAccessibilityNode = ClearFocusedFocusAccessibilityNode;
230    accessibilityProviderCallbacks_->getAccessibilityNodeCursorPosition = GetAccessibilityNodeCursorPosition;
231    ret = OH_ArkUI_AccessibilityProviderRegisterCallback(provider_, accessibilityProviderCallbacks_);
232    if (ret != 0) {
233        return;
234    }
235}
236```
237
238
239
2404.组件发生变化时,主动发送事件。参考事件定义[ArkUI_AccessibilityEventType](../reference/apis-arkui/capi-native-interface-accessibility-h.md#arkui_accessibilityeventtype)说明。
241
242如果因为Touch事件导致页面变化,需要发送页面变化事件ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_STATE_UPDATE以及获焦组件位置变化事件ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_FOCUS_NODE_UPDATE给无障碍子系统。
243
244```C
245// 定义一个函数DispatchTouchEventCB(),响应触摸事件时触发该回调
246void DispatchTouchEventCB(OH_NativeXComponent *component, void *window)
247{
248	// ...
249	// 获取XComponent的id
250	char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = { '\0' };
251	uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1;
252	if (OH_NativeXComponent_GetXComponentId(component, idStr, &idSize) != OH_NATIVEXCOMPONENT_RESULT_SUCCESS) {
253		OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
254			"DispatchTouchEventCB: Unable to get XComponent id");
255		return;
256	}
257
258    // 判断当前是否已注册无障碍provider
259    if (provider_ != nullptr) {
260
261        // 需判断当前Touch事件是否引起了页面变化和当前获焦组件的位置变化。若引起变化,则需要上报无障碍事件,通知无障碍服务以及辅助应用。
262        // ...
263        // 明确当前上报事件的组件节点为node
264        // 1.调用OH_ArkUI_CreateAccessibilityEventInfo创建ArkUI_AccessibilityEventInfo
265        ArkUI_AccessibilityEventInfo *eventInfo = OH_ArkUI_CreateAccessibilityEventInfo();
266        if (eventInfo == nullptr) {
267            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
268			"DispatchTouchEventCB: Unable to create accessibility eventInfo");
269            return;
270        }
271        // 2.调用OH_ArkUI_CreateAccessibilityElementInfo创建ArkUI_AccessibilityElementInfo
272        ArkUI_AccessibilityElementInfo *elementInfo = OH_ArkUI_CreateAccessibilityElementInfo();
273        if (elementInfo == nullptr) {
274            OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Callback",
275			"DispatchTouchEventCB: Unable to create accessibility elementInfo");
276            return;
277        }
278        // 3.填写element内容
279        // 设置element成员内容
280    	OH_ArkUI_AccessibilityElementInfoSetElementId(element, nodes[i].id);
281    	OH_ArkUI_AccessibilityElementInfoSetComponentType(element, nodes[i].type);
282        // ...
283
284        OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "Callback",
285			"DispatchTouchEventCB: send accessibility event");
286        // 4.发送页面更新事件
287        SendAccessibilityAsyncEvent(eventInfo, elementInfo, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_STATE_UPDATE);
288        // 5.如当前处理引起了获焦组件的位置变化,发送获焦位置变化事件
289        SendAccessibilityAsyncEvent(eventInfo, elementInfo, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_FOCUS_NODE_UPDATE);
290        // 6.销毁eventInfo,elementInfo内存
291        OH_ArkUI_DestoryAccessibilityElementInfo(elementInfo);
292        OH_ArkUI_DestoryAccessibilityEventInfo(eventInfo);
293    }
294
295	std::string id(idStr);
296	PluginRender *render = PluginRender::GetInstance(id);
297	if (render != nullptr) {
298		// 封装OnTouchEvent方法
299		render->OnTouchEvent(component, window);
300	}
301}
302
303void FillEvent(ArkUI_AccessibilityEventInfo *eventInfo, ArkUI_AccessibilityElementInfo *elementInfo, ArkUI_AccessibilityEventType eventType)
304{
305    if (eventInfo == nullptr) {
306        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_TEXT, "eventInfo is null");
307        return;
308    }
309    if (elementInfo == nullptr) {
310        OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_TEXT, "elementInfo is null");
311        return;
312    }
313    // 1.设置事件类型
314    OH_ArkUI_AccessibilityEventSetEventType(eventInfo, eventType);
315    // 2.设置发送事件的节点组件信息
316    OH_ArkUI_AccessibilityEventSetElementInfo(eventInfo, elementInfo);
317
318}
319void SendAccessibilityAsyncEvent(ArkUI_AccessibilityEventInfo *eventInfo, ArkUI_AccessibilityElementInfo *elementInfo, ArkUI_AccessibilityEventType eventType)
320{
321    // 1.填写event内容
322    FillEvent(eventInfo, elementInfo, ArkUI_AccessibilityEventType::ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_PAGE_STATE_UPDATE);
323    // 2.设置创建callback函数,获取发送事件结果
324    auto callback = [](int32_t errorCode){
325         OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "result: %{public}d", errorCode);
326    };
327    // 3. 调用接口发送事件给无障碍子系统
328    OH_ArkUI_SendAccessibilityAsyncEvent(provider_, eventInfo, callback);
329}
330```
331
3325.对接成功后,可开启无障碍功能。
333
334![accessibility](./figures/accessibility-pic.png)
335