通过自绘制接入无障碍
通过自定义绘制接入的第三方框架平台,NDK提供了对接无障碍服务的接口函数,使三方框架组件能够支持ArkUI中的基本无障碍功能,包括焦点获取、获取无障碍节点和操作响应。
从API version 13开始,支持基于Xcomponent的自绘制方式接入。
从API version 23开始,支持基于CustomNode构建渲染节点树的自绘制方式接入。
三方框架从绘制容器组件获取到ArkUI_AccessibilityProvider,通过实现其中对接无障碍的回调函数,来适配无障碍系统发出的操作Action,并针对组件交互行为发送无障碍事件Event到无障碍子系统,实现无障碍辅助应用的交互体验。
基于Xcomponent的自绘制方式接入方式通过OH_NativeXComponent_GetNativeAccessibilityProvider获得无障碍接入ArkUI_AccessibilityProvider。
基于CustomNode构建渲染节点树的自绘制方式接入方式通过OH_ArkUI_NativeModule_GetNativeAccessibilityProvider获得无障碍接入ArkUI_AccessibilityProvider。
如果需要支持单实例,通过OH_ArkUI_AccessibilityProviderRegisterCallback注册接入无障碍所需的回调函数ArkUI_AccessibilityProviderCallbacks。
如果需要支持多实例,则通过OH_ArkUI_AccessibilityProviderRegisterCallbackWithInstance注册接入无障碍所需的回调函数ArkUI_AccessibilityProviderCallbacksWithInstance。
- 无障碍能力:指开发者能够创建可访问的应用界面,满足视觉、听觉、运动和认知障碍等用户需求的能力。
- 实现OH_ArkUI_AccessibilityProviderRegisterCallback或者OH_ArkUI_AccessibilityProviderRegisterCallbackWithInstance回调查询接口时,查询到的每个无障碍节点信息通过OH_ArkUI_AddAndGetAccessibilityElementInfo创建分配element内存,并将其加入到指定的elementList中。
- 使用OH_ArkUI_SendAccessibilityAsyncEvent发送事件时,需要使用OH_ArkUI_CreateAccessibilityEventInfo创建ArkUI_AccessibilityEventInfo,使用OH_ArkUI_CreateAccessibilityElementInfo创建ArkUI_AccessibilityElementInfo,使用结束后,需要调用OH_ArkUI_DestoryAccessibilityEventInfo以及OH_ArkUI_DestoryAccessibilityElementInfo销毁函数释放内存。
- 回调函数打印日志时,携带输入的requestId,用于关联一次交互过程相关的日志,便于索引查询整个流程,协助问题定位。
基于Xcomponent的自绘制接入方式
以下示例提供了对接无障碍能力的实现方法,仅包含主要步骤,完整示例请参考AccessibilityCapiSample。对接完成后,在开启无障碍功能时,可使XComponent中的三方框架绘制组件接入,实现无障碍交互。
-
按照自定义渲染(XComponent)的使用OH_ArkUI_SurfaceHolder管理Surface生命周期场景创建前置工程。
-
获得无障碍接入provider并注册回调函数(以多实例场景为例)。
#include <arkui/native_interface_accessibility.h>#include <string>#include "common/common.h"// 完整实现请参考AccessibilityCapiSample。#include "fakenode/fake_node.h"// 完整实现请参考AccessibilityCapiSample。#include "AccessibilityManager.h"// ...AccessibilityManager::AccessibilityManager(){// 多实例场景accessibilityProviderCallbacksWithInstance_.findAccessibilityNodeInfosById = FindAccessibilityNodeInfosById;accessibilityProviderCallbacksWithInstance_.findAccessibilityNodeInfosByText = FindAccessibilityNodeInfosByText;accessibilityProviderCallbacksWithInstance_.findFocusedAccessibilityNode = FindFocusedAccessibilityNode;accessibilityProviderCallbacksWithInstance_.findNextFocusAccessibilityNode = FindNextFocusAccessibilityNode;accessibilityProviderCallbacksWithInstance_.executeAccessibilityAction = ExecuteAccessibilityAction;accessibilityProviderCallbacksWithInstance_.clearFocusedFocusAccessibilityNode = ClearFocusedFocusAccessibilityNode;accessibilityProviderCallbacksWithInstance_.getAccessibilityNodeCursorPosition = GetAccessibilityNodeCursorPosition;// 单实例场景accessibilityProviderCallbacks_.findAccessibilityNodeInfosById = FindAccessibilityNodeInfosById;accessibilityProviderCallbacks_.findAccessibilityNodeInfosByText = FindAccessibilityNodeInfosByText;accessibilityProviderCallbacks_.findFocusedAccessibilityNode = FindFocusedAccessibilityNode;accessibilityProviderCallbacks_.findNextFocusAccessibilityNode = FindNextFocusAccessibilityNode;accessibilityProviderCallbacks_.executeAccessibilityAction = ExecuteAccessibilityAction;accessibilityProviderCallbacks_.clearFocusedFocusAccessibilityNode = ClearFocusedFocusAccessibilityNode;accessibilityProviderCallbacks_.getAccessibilityNodeCursorPosition = GetAccessibilityNodeCursorPosition;}void AccessibilityManager::Initialize(const std::string &id, OH_NativeXComponent *nativeXComponent){int32_t ret = OH_NativeXComponent_GetNativeAccessibilityProvider(nativeXComponent, &provider);if (provider == nullptr) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "get provider is null");return;}// 2.注册回调函数ret = OH_ArkUI_AccessibilityProviderRegisterCallbackWithInstance(id.c_str(), provider,&accessibilityProviderCallbacksWithInstance_);if (ret != 0) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"InterfaceDesignTest OH_ArkUI_AccessibilityProviderRegisterCallback failed");return;}g_provider = provider;}// ... -
三方框架需要实现如下回调函数。
-
基于指定的节点,查询所需的节点信息
- 当无障碍系统传入的elementId为-1时,代表其约定的“根节点标识”。三方框架需将该外部标识,映射为自身内部节点树中唯一根节点的ID,以便无障碍系统正确定位到框架的根节点。
- 三方框架需要提供一个且仅包含一个根节点。
- 根节点的属性中的parentId须设置为特殊值-2100000。在无障碍树中,根节点是最顶层节点,没有父节点。这个特殊值-2100000是ArkUI无障碍框架的硬编码约定,用于明确标识 “此节点为根节点,无父节点”。使用一个特殊值而非0或-1,是为了防止与三方框架内部的有效ID产生冲突,确保系统能准确识别根节点。
- 根节点的属性中的enabled须设置为true。如果设置为false,根节点被禁用,无障碍系统会认为整个控件树都不可交互,从而忽略所有子节点的查询和操作。根节点作为整个控件树的入口,必须处于可用状态,才能保证无障碍服务正常工作。
- 根节点的属性中的visible须设置为true。无障碍系统只对可见的节点进行遍历和交互。如果设置为false,根节点不可见,整个控件树都会被无障碍服务忽略,导致三方框架的无障碍能力完全失效。确保用户在使用无障碍功能时,能感知到三方框架渲染的所有界面元素。
int32_t AccessibilityManager::FindAccessibilityNodeInfosById(const char* instanceId, int64_t elementId,ArkUI_AccessibilitySearchMode mode, int32_t requestId, ArkUI_AccessibilityElementInfoList *elementList){OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"FindAccessibilityNodeInfosById start,instanceId %{public}s elementId: %{public}ld, ""requestId: %{public}d, mode: %{public}d", instanceId,elementId, requestId, static_cast<int32_t>(mode));if (elementList == nullptr) {OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"FindAccessibilityNodeInfosById elementList is null");return OH_NATIVEXCOMPONENT_RESULT_FAILED;}int ret = 0;const int parentOfRoot = -2100000;if (elementId == -1) {elementId = 0;}if (mode == ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_RECURSIVE_CHILDREN) {// 三方框架需要在该方法中实现自己的查找策略,返回无障碍节点信息给无障碍服务,以下逻辑仅为示意过程。// ArkUI框架设计的特殊值,根节点必须设置parentId为这个值。auto rootNode = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);if (!rootNode) {return OH_NATIVEXCOMPONENT_RESULT_FAILED;}// 设置根节点信息OH_ArkUI_AccessibilityElementInfoSetElementId(rootNode, 0);OH_ArkUI_AccessibilityElementInfoSetParentId(rootNode, parentOfRoot);FakeWidget::Instance().fillAccessibilityElement(rootNode);ArkUI_AccessibleRect rect;rect.leftTopX = NUMBER_ZERO;rect.leftTopY = NUMBER_ZERO;rect.rightBottomX = NUMBER_THIRD;rect.rightBottomY = NUMBER_THIRD;ret = OH_ArkUI_AccessibilityElementInfoSetScreenRect(rootNode, &rect);// 设置根节点不可被无障碍辅助服务所识别。OH_ArkUI_AccessibilityElementInfoSetAccessibilityLevel(rootNode, "no");auto objects = FakeWidget::Instance().GetAllObjects(instanceId);int64_t childNodes[1024];for (int i = 0; i < objects.size(); i++) {int elementId = i + 1;childNodes[i] = elementId;}for (int i = 0; i < objects.size(); i++) {int elementId = i + 1;childNodes[i] = elementId;auto child = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);// 设置子节点信息。OH_ArkUI_AccessibilityElementInfoSetElementId(child, elementId);OH_ArkUI_AccessibilityElementInfoSetParentId(child, 0);// 设置当前组件可被无障碍辅助服务所识别。OH_ArkUI_AccessibilityElementInfoSetAccessibilityLevel(child, "yes");objects[i]->fillAccessibilityElement(child);ArkUI_AccessibleRect rect;rect.leftTopX = i * NUMBER_FIRST;rect.leftTopY = NUMBER_FIRST;rect.rightBottomX = i * NUMBER_FIRST + NUMBER_FIRST;rect.rightBottomY = NUMBER_SECOND;OH_ArkUI_AccessibilityElementInfoSetScreenRect(child, &rect);if (objects[i]->ObjectType() == "FakeSlider") {auto rangeInfo = objects[i]->GetRangeInfo();OH_ArkUI_AccessibilityElementInfoSetRangeInfo(child, &rangeInfo);}if (objects[i]->ObjectType() == "FakeList") {auto gridInfo = objects[i]->GetGridInfo();OH_ArkUI_AccessibilityElementInfoSetGridInfo(child, &gridInfo);}if (objects[i]->ObjectType() == "FakeSwiper") {auto gridItemInfo = objects[i]->GetGridItemInfo();OH_ArkUI_AccessibilityElementInfoSetGridItemInfo(child, &gridItemInfo);}}ret = OH_ArkUI_AccessibilityElementInfoSetChildNodeIds(rootNode, objects.size(), childNodes);OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"FindAccessibilityNodeInfosById child count: %{public}ld %{public}d",objects.size(), ret);} else if (mode == ARKUI_ACCESSIBILITY_NATIVE_SEARCH_MODE_PREFETCH_CURRENT) {auto &widget = FakeWidget::Instance();AccessibleObject *obj = nullptr;if (elementId == 0) {obj = &widget;} else {obj = widget.GetChild(elementId);}if (!obj) {return OH_NATIVEXCOMPONENT_RESULT_FAILED;}auto node = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);OH_ArkUI_AccessibilityElementInfoSetElementId(node, elementId);OH_ArkUI_AccessibilityElementInfoSetParentId(node, elementId == 0 ? parentOfRoot : 0);OH_ArkUI_AccessibilityElementInfoSetAccessibilityLevel(node, elementId == 0 ? "no" : "yes");obj->fillAccessibilityElement(node);ArkUI_AccessibleRect rect;if (elementId == 0) {rect.leftTopX = NUMBER_ZERO;rect.leftTopY = NUMBER_ZERO;rect.rightBottomX = NUMBER_THIRD;rect.rightBottomY = NUMBER_THIRD;} else {int i = elementId - 1;rect.leftTopX = i * NUMBER_FIRST;rect.leftTopY = NUMBER_FIRST;rect.rightBottomX = i * NUMBER_FIRST + NUMBER_FIRST;rect.rightBottomY = NUMBER_SECOND;}OH_ArkUI_AccessibilityElementInfoSetScreenRect(node, &rect);if (elementId == 0) {auto objects = FakeWidget::Instance().GetAllObjects(instanceId);int64_t childNodes[1024];for (int i = 0; i < objects.size(); i++) {int elementId = i + 1;childNodes[i] = elementId;auto child = OH_ArkUI_AddAndGetAccessibilityElementInfo(elementList);OH_ArkUI_AccessibilityElementInfoSetElementId(child, elementId);OH_ArkUI_AccessibilityElementInfoSetParentId(child, 0);objects[i]->fillAccessibilityElement(child);ArkUI_AccessibleRect rect;rect.leftTopX = i * NUMBER_FIRST;rect.leftTopY = NUMBER_ZERO;rect.rightBottomX = i * NUMBER_FIRST + NUMBER_FIRST;rect.rightBottomY = NUMBER_SECOND;OH_ArkUI_AccessibilityElementInfoSetScreenRect(child, &rect);}ret = OH_ArkUI_AccessibilityElementInfoSetChildNodeIds(node, objects.size(), childNodes);OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"FindAccessibilityNodeInfosById child2 count: %{public}ld", objects.size());}}OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "FindAccessibilityNodeInfosById end");return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;} -
基于指定的节点,查询下一个可聚焦的无障碍节点
int32_t AccessibilityManager::FindNextFocusAccessibilityNode(const char* instanceId, int64_t elementId,ArkUI_AccessibilityFocusMoveDirection direction, int32_t requestId,ArkUI_AccessibilityElementInfo *elementInfo){// 查找下一个可聚焦的无障碍节点,三方框架需要在该方法中实现自己的查找策略,以下逻辑仅为示意过程。OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"FindNextFocusAccessibilityNode instanceId %{public}s ""elementId: %{public}ld, requestId: %{public}d, direction: %{public}d",instanceId, elementId, requestId, static_cast<int32_t>(direction));auto objects = FakeWidget::Instance().GetAllObjects(instanceId);OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "objects.size() %{public}d", objects.size());// object.size 不包含 root节点if ((elementId < 0) || (elementId > objects.size())) {OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "elementId invalid");return OH_NATIVEXCOMPONENT_RESULT_FAILED;}int64_t nextElementId = -1;if (direction == ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_FORWARD) {nextElementId = elementId + 1;} else {nextElementId = elementId - 1;}// 屏幕朗读约束 如果是根节点 然后backward的话需要回到最后一个节点if ((nextElementId == -1) && (direction == ARKUI_ACCESSIBILITY_NATIVE_DIRECTION_BACKWARD)) {nextElementId = objects.size();}if (nextElementId > objects.size()) {OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "nextElementId invalid");return OH_NATIVEXCOMPONENT_RESULT_FAILED;}if (nextElementId <= 0) {OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "nextElementId less than zero");return OH_NATIVEXCOMPONENT_RESULT_FAILED;}OH_ArkUI_AccessibilityElementInfoSetElementId(elementInfo, nextElementId);OH_ArkUI_AccessibilityElementInfoSetParentId(elementInfo, 0);// id 比object索引大1objects[nextElementId - 1]->fillAccessibilityElement(elementInfo);ArkUI_AccessibleRect rect;rect.leftTopX = nextElementId * NUMBER_FIRST;rect.leftTopY = NUMBER_ZERO;rect.rightBottomX = nextElementId * NUMBER_FIRST + NUMBER_FIRST;rect.rightBottomY = NUMBER_SECOND;OH_ArkUI_AccessibilityElementInfoSetScreenRect(elementInfo, &rect);auto eventInfo = OH_ArkUI_CreateAccessibilityEventInfo();OH_ArkUI_AccessibilityEventSetRequestFocusId(eventInfo, requestId);OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "%{public}ld", nextElementId);return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;} -
基于指定的节点,查询满足指定组件文本内容的节点信息
int32_t AccessibilityManager::FindAccessibilityNodeInfosByText(const char* instanceId, int64_t elementId,const char *text, int32_t requestId, ArkUI_AccessibilityElementInfoList *elementList){// 三方框架需实现根据文本内容查询无障碍节点的逻辑。OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"FindAccessibilityNodeInfosByText start,instanceId %{public}s elementId: %{public}ld, ""requestId: %{public}d, text: %{public}s.", instanceId,elementId, requestId, text);return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;} -
基于指定的节点,查询已经聚焦的节点信息
int32_t AccessibilityManager::FindFocusedAccessibilityNode(const char* instanceId, int64_t elementId,ArkUI_AccessibilityFocusType focusType, int32_t requestId, ArkUI_AccessibilityElementInfo *elementInfo){// 三方框架需实现基于指定节点获取焦点元素信息的逻辑。OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"FindFocusedAccessibilityNode start instanceId %{public}s, ""elementId: %{public}ld, requestId: %{public}d, focusType: %{public}d",instanceId, elementId, requestId, static_cast<int32_t>(focusType));return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;} -
基于指定的节点,执行指定的操作
void FillEvent(ArkUI_AccessibilityEventInfo *eventInfo, ArkUI_AccessibilityElementInfo *elementInfo,ArkUI_AccessibilityEventType eventType, std::string announcedText){if (eventInfo == nullptr) {return;}if (elementInfo == nullptr) {return;}// 设置事件类型OH_ArkUI_AccessibilityEventSetEventType(eventInfo, eventType);// 设置事件对应的元素信息OH_ArkUI_AccessibilityEventSetElementInfo(eventInfo, elementInfo);if (eventType == ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ANNOUNCE_FOR_ACCESSIBILITY && announcedText.size() > 0) {// 给无障碍节点设置优先播报的无障碍文本OH_ArkUI_AccessibilityEventSetTextAnnouncedForAccessibility(eventInfo, announcedText.data());}}// ...void AccessibilityManager::SendAccessibilityAsyncEvent(ArkUI_AccessibilityElementInfo *elementInfo,ArkUI_AccessibilityEventType eventType,std::string announcedText){auto eventInfo = OH_ArkUI_CreateAccessibilityEventInfo();// 1.填写event内容FillEvent(eventInfo, elementInfo, eventType, announcedText);// 2.callbackauto callback = [](int32_t errorCode) {OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT, "result: %{public}d", errorCode);};// 3. 调用接口发送事件给OH侧OH_ArkUI_SendAccessibilityAsyncEvent(g_provider, eventInfo, callback);}// ...int32_t AccessibilityManager::ExecuteAccessibilityAction(const char* instanceId, int64_t elementId,ArkUI_Accessibility_ActionType action, ArkUI_AccessibilityActionArguments *actionArguments, int32_t requestId){// 三方框架需要实现执行无障碍节点行为的逻辑。OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"ExecuteAccessibilityAction instanceId %{public}s elementId: %{public}ld, ""action: %{public}d, requestId: %{public}d",instanceId, elementId, action, requestId);auto object = FakeWidget::Instance().GetChild(elementId);// 传入的无障碍节点对象可能为空,需要做非空判断。if (!object) {return 0;}// 获取无障碍节点element。auto announcedText = object->GetAnnouncedForAccessibility();auto element = OH_ArkUI_CreateAccessibilityElementInfo();OH_ArkUI_AccessibilityElementInfoSetElementId(element, elementId);const char *actionKey = "some_key";char *actionValue = nullptr;OH_ArkUI_FindAccessibilityActionArgumentByKey(actionArguments, actionKey, &actionValue);// 根据action类型执行对应的行为。switch (action) {case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLICK:if (object) {object->OnClick();object->fillAccessibilityElement(element);}// 向无障碍服务发送指定事件。AccessibilityManager::SendAccessibilityAsyncEvent(element,ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_CLICKED, announcedText);break;case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_GAIN_ACCESSIBILITY_FOCUS:if (object) {object->SetFocus(true);object->fillAccessibilityElement(element);}// 向无障碍服务发送指定事件。AccessibilityManager::SendAccessibilityAsyncEvent(element,ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUSED,announcedText);break;case ARKUI_ACCESSIBILITY_NATIVE_ACTION_TYPE_CLEAR_ACCESSIBILITY_FOCUS:if (object) {object->SetFocus(false);object->fillAccessibilityElement(element);}AccessibilityManager::SendAccessibilityAsyncEvent(element, ARKUI_ACCESSIBILITY_NATIVE_EVENT_TYPE_ACCESSIBILITY_FOCUS_CLEARED,announcedText);break;default:// 处理不支持的action行为。break;}OH_ArkUI_DestoryAccessibilityElementInfo(element);return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;} -
清除当前获焦的节点
int32_t AccessibilityManager::ClearFocusedFocusAccessibilityNode(const char* instanceId){// 三方框架需要实现清除当前获焦的节点的行为。OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"ClearFocusedFocusAccessibilityNode, instanceId %{public}s", instanceId);return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;} -
基于指定的节点,获取当前文本组件的光标位置
int32_t AccessibilityManager::GetAccessibilityNodeCursorPosition(const char* instanceId, int64_t elementId,int32_t requestId, int32_t *index){// 三方框架需要实现获取当前组件中(文本组件)光标位置。OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, LOG_PRINT_TEXT,"GetAccessibilityNodeCursorPosition, instanceId %{public}s ""elementId: %{public}ld, requestId: %{public}d, index: %{public}d",instanceId, elementId, requestId, index);return OH_NATIVEXCOMPONENT_RESULT_SUCCESS;}
- provider通过回调函数OH_ArkUI_AccessibilityProviderRegisterCallback或者OH_ArkUI_AccessibilityProviderRegisterCallbackWithInstance对接成功后,可开启无障碍功能。
基于CustomNode的自绘制接入方式
- 基于CustomNode的自定义绘制容器组件,仅支持类型为ARKUI_NODE_CUSTOM且无其他子节点的native组件。绘制容器组件的宽和高不能为0,避免被无障碍辅助应用忽略或错误处理子节点树。
以下示例提供了对接无障碍能力的实现方法,仅包含主要步骤,完整示例请参考AccessibilityCustomCapi。回调函数实现请参考基于Xcomponent的自绘制接入方式。完成回调函数实现后,开启无障碍功能,基于CustomNode构建渲染节点树的三方框架即可接入无障碍服务,实现控件树的无障碍交互与信息查询。
-
按照基于CustomNode构建渲染节点树场景创建前置工程。
-
获取无障碍接入Provider实例,将回调函数与Provider实例绑定并完成注册。
int32_t AccessibilityMaker::GetAccessibilityProvider(ArkUI_NodeHandle* customNode, const char* id){AccessibilityMaker::accessibilityProviderCallbacksWithInstance_.findAccessibilityNodeInfosById =FindAccessibilityNodeInfosById;AccessibilityMaker::accessibilityProviderCallbacksWithInstance_.findAccessibilityNodeInfosByText =FindAccessibilityNodeInfosByText;AccessibilityMaker::accessibilityProviderCallbacksWithInstance_.findFocusedAccessibilityNode =FindFocusedAccessibilityNode;AccessibilityMaker::accessibilityProviderCallbacksWithInstance_.findNextFocusAccessibilityNode =FindNextFocusAccessibilityNode;AccessibilityMaker::accessibilityProviderCallbacksWithInstance_.executeAccessibilityAction =ExecuteAccessibilityAction;AccessibilityMaker::accessibilityProviderCallbacksWithInstance_.clearFocusedFocusAccessibilityNode =ClearFocusedFocusAccessibilityNode;AccessibilityMaker::accessibilityProviderCallbacksWithInstance_.getAccessibilityNodeCursorPosition =GetAccessibilityNodeCursorPosition;// 获取 native 层提供的 accessibility provider,并为其注册回调OH_ArkUI_NativeModule_GetNativeAccessibilityProvider(customNode, &accessibilityProvider_);if (accessibilityProvider_ == nullptr) {OH_LOG_Print(LOG_APP, LOG_INFO, LOG_PRINT_DOMAIN, "AccessibilityMaker", "accessibilityProvider_ is null");return 0;}int32_t ret = OH_ArkUI_AccessibilityProviderRegisterCallbackWithInstance(id, accessibilityProvider_,&AccessibilityMaker::accessibilityProviderCallbacksWithInstance_);if (ret != 0) {return 0;}return 0;} -
三方框架需要实现回调函数。请参考前文的基于Xcomponent的自绘制接入方式。