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 328