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