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 335