1# 构建渲染节点 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @xiang-shouxing--> 5<!--Designer: @xiang-shouxing--> 6<!--Tester: @sally__--> 7<!--Adviser: @HelloCrease--> 8 9 10从API version 20开始,ArkUI开发框针对NDK接口,提供了直接构建渲染节点的能力,包括节点树操作、属性设置及含动画的自定义绘制。开发者通过调用渲染节点相关能力,可以绕过[registerNodeCustomEvent](../reference/apis-arkui/capi-arkui-nativemodule-arkui-nativenodeapi-1.md#registernodecustomevent)的测量布局过程,直接对节点进行绘制并调整其大小和位置。 11 12 13- **渲染节点树操作相关的能力** ,例如[OH_ArkUI_RenderNodeUtils_AddRenderNode](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_addrendernode)、[OH_ArkUI_RenderNodeUtils_AddChild](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_addchild)等接口用于编辑渲染节点树的结构。仅类型为ARKUI_NODE_CUSTOM且无其他子节点的自定义节点(加超链接到nativeNode)能够挂载渲染节点,且最多挂载一个渲染节点。即渲染节点以子树形式挂载在类型为ARKUI_NODE_CUSTOM的叶子自定义节点上。 14 15- **渲染节点属性设置的能力** ,详情请参考[函数](../reference/apis-arkui/capi-native-render-h.md#函数),查看当前渲染节点支持的属性能力。 16 17- **自定义绘制能力**,参考[OH_ArkUI_RenderNodeUtils_SetContentModifierOnDraw](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_setcontentmodifierondraw)及其相关接口,同时可以通过[OH_ArkUI_RenderNodeUtils_SetFloatPropertyValue](../reference/apis-arkui/capi-native-render-h.md#oh_arkui_rendernodeutils_setfloatpropertyvalue)这一类绑定在ContentModifier的接口对自定义绘制的内容进行动态修改。 18 19 20 21## 节点挂载与基础属性设置 22 23以下示例创建了一个渲染节点,并进行了基础的节点挂载和属性设置操作。 24 251. 按照[接入ArkTS页面](ndk-access-the-arkts-page.md)创建前置工程。 26 272. 创建渲染节点能力对象。 28 ```c 29 // manager.cpp 30 // 自定义容器组件示例。 31 #include <arkui/native_animate.h> 32 #include <arkui/native_render.h> 33 #include <arkui/native_type.h> 34 #include <arkui/native_node_napi.h> 35 #include <bits/alltypes.h> 36 37 #include <string> 38 39 #include <arkui/native_interface.h> 40 #include <arkui/native_node.h> 41 #include <native_drawing/drawing_canvas.h> 42 #include <native_drawing/drawing_color.h> 43 #include <native_drawing/drawing_path.h> 44 #include <native_drawing/drawing_pen.h> 45 46 ArkUI_NodeHandle testRenderNode(ArkUI_NativeNodeAPI_1 *nodeAPI) { 47 // 创建CAPI原有容器逻辑。 48 ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); 49 ArkUI_NumberValue valueWidth[] = {400}; 50 ArkUI_AttributeItem itemWidth = {valueWidth, sizeof(valueWidth) / sizeof(ArkUI_NumberValue)}; 51 nodeAPI->setAttribute(scroll, NODE_WIDTH, &itemWidth); 52 ArkUI_NumberValue valueHeight[] = {600}; 53 ArkUI_AttributeItem itemHeight = {valueHeight, sizeof(valueHeight) / sizeof(ArkUI_NumberValue)}; 54 nodeAPI->setAttribute(scroll, NODE_HEIGHT, &itemHeight); 55 ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); 56 nodeAPI->setAttribute(column, NODE_WIDTH, &itemWidth); 57 nodeAPI->setAttribute(column, NODE_HEIGHT, &itemHeight); 58 ArkUI_NodeHandle text = nodeAPI->createNode(ARKUI_NODE_TEXT); 59 ArkUI_AttributeItem content = {.string = "黄色背景是C-API页面"}; 60 nodeAPI->setAttribute(text, NODE_TEXT_CONTENT, &content); 61 nodeAPI->addChild(column, text); 62 63 // 创建RenderNode容器 -- NDK侧的Custom组件。 64 ArkUI_NodeHandle Custom = nodeAPI->createNode(ARKUI_NODE_CUSTOM); 65 valueWidth[0].f32 = 400; 66 nodeAPI->setAttribute(Custom, NODE_WIDTH, &itemWidth); 67 nodeAPI->setAttribute(Custom, NODE_HEIGHT, &itemWidth); 68 nodeAPI->addChild(column, Custom); 69 70 // 节点操作类接口 创建 - 挂载 - 构建树。 71 // 创建部分。 72 auto renderRootNode = OH_ArkUI_RenderNodeUtils_CreateNode(); 73 auto firstChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); 74 auto secondChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); 75 auto thirdChildRenderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); 76 77 auto result = OH_ArkUI_RenderNodeUtils_AddRenderNode(Custom, renderRootNode); 78 if (result != ARKUI_ERROR_CODE_NO_ERROR) { 79 // 通过错误码判断根节点是否挂载成功。 80 return scroll; 81 } 82 83 OH_ArkUI_RenderNodeUtils_AddChild(renderRootNode, firstChildRenderNode); 84 OH_ArkUI_RenderNodeUtils_AddChild(renderRootNode, secondChildRenderNode); 85 OH_ArkUI_RenderNodeUtils_AddChild(renderRootNode, thirdChildRenderNode); 86 87 // 设置节点尺寸与位置。 88 OH_ArkUI_RenderNodeUtils_SetSize(renderRootNode, 500, 500); 89 OH_ArkUI_RenderNodeUtils_SetSize(firstChildRenderNode, 120, 120); 90 OH_ArkUI_RenderNodeUtils_SetSize(secondChildRenderNode, 120, 120); 91 OH_ArkUI_RenderNodeUtils_SetSize(thirdChildRenderNode, 120, 120); 92 93 OH_ArkUI_RenderNodeUtils_SetPosition(renderRootNode, 300, 100); 94 OH_ArkUI_RenderNodeUtils_SetPosition(firstChildRenderNode, 0, 0); 95 OH_ArkUI_RenderNodeUtils_SetPosition(secondChildRenderNode, 140, 140); 96 OH_ArkUI_RenderNodeUtils_SetPosition(thirdChildRenderNode, 280, 280); 97 98 // 设置颜色,方便通过颜色观察到节点的显示范围。 99 OH_ArkUI_RenderNodeUtils_SetBackgroundColor(renderRootNode, 0xFFFFFFFF); 100 OH_ArkUI_RenderNodeUtils_SetBackgroundColor(firstChildRenderNode, 0xFFFF0000); // R 101 OH_ArkUI_RenderNodeUtils_SetBackgroundColor(secondChildRenderNode, 0xFF00FF00); // G 102 OH_ArkUI_RenderNodeUtils_SetBackgroundColor(thirdChildRenderNode, 0xFF0000FF); // B 103 104 // 简单的属性设置示例。 105 OH_ArkUI_RenderNodeUtils_SetRotation(secondChildRenderNode, 45, 45, 0); // xy轴旋转45度,z轴旋转0度 106 107 // 边框属性实例。 108 auto styleOption = OH_ArkUI_RenderNodeUtils_CreateNodeBorderStyleOption(); 109 OH_ArkUI_RenderNodeUtils_SetNodeBorderStyleOptionEdgeStyle(styleOption, ArkUI_BorderStyle::ARKUI_BORDER_STYLE_SOLID, 110 ArkUI_EdgeDirection::ARKUI_EDGE_DIRECTION_ALL); 111 OH_ArkUI_RenderNodeUtils_SetBorderStyle(firstChildRenderNode, styleOption); 112 // 结构体使用完成后,销毁释放内存。 113 OH_ArkUI_RenderNodeUtils_DisposeNodeBorderStyleOption(styleOption); 114 styleOption = nullptr; 115 116 auto widthOption = OH_ArkUI_RenderNodeUtils_CreateNodeBorderWidthOption(); 117 OH_ArkUI_RenderNodeUtils_SetNodeBorderWidthOptionEdgeWidth(widthOption, 5, 118 ArkUI_EdgeDirection::ARKUI_EDGE_DIRECTION_ALL); 119 OH_ArkUI_RenderNodeUtils_SetBorderWidth(firstChildRenderNode, widthOption); 120 // 结构体使用完成后,销毁释放内存。 121 OH_ArkUI_RenderNodeUtils_DisposeNodeBorderWidthOption(widthOption); 122 widthOption = nullptr; 123 124 auto colorOption = OH_ArkUI_RenderNodeUtils_CreateNodeBorderColorOption(); 125 OH_ArkUI_RenderNodeUtils_SetNodeBorderColorOptionEdgeColor(colorOption, 0xFF000000, 126 ArkUI_EdgeDirection::ARKUI_EDGE_DIRECTION_ALL); 127 result = OH_ArkUI_RenderNodeUtils_SetBorderColor(firstChildRenderNode, colorOption); 128 // 结构体使用完成后,销毁释放内存。 129 OH_ArkUI_RenderNodeUtils_DisposeNodeBorderColorOption(colorOption); 130 colorOption = nullptr; 131 132 nodeAPI->addChild(scroll, column); 133 return scroll; 134 } 135 136 napi_value Manager::CreateNativeNode(napi_env env, napi_callback_info info) { 137 if ((env == nullptr) || (info == nullptr)) { 138 return nullptr; 139 } 140 size_t argCnt = 2; 141 napi_value args[2] = {nullptr}; 142 if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) { 143 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode napi_get_cb_info failed"); 144 } 145 146 if (argCnt != 2) { 147 napi_throw_type_error(env, NULL, "Wrong number of arguments"); 148 return nullptr; 149 } 150 napi_valuetype valuetype; 151 if (napi_typeof(env, args[0], &valuetype) != napi_ok) { 152 napi_throw_type_error(env, NULL, "napi_typeof failed"); 153 return nullptr; 154 } 155 156 if (valuetype != napi_string) { 157 napi_throw_type_error(env, NULL, "Wrong type of arguments"); 158 return nullptr; 159 } 160 161 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; 162 constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 163 size_t length; 164 if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) { 165 napi_throw_type_error(env, NULL, "napi_get_value_int64 failed"); 166 return nullptr; 167 } 168 169 auto manager = Manager::GetInstance(); 170 if (manager == nullptr) { 171 return nullptr; 172 } 173 174 OH_NativeXComponent *component = manager->GetNativeXComponent(idStr); 175 if (component == nullptr) { 176 return nullptr; 177 } 178 179 ArkUI_NodeHandle xxxNode; 180 auto resultx = OH_ArkUI_NodeUtils_GetNodeHandleByUniqueId(3, &xxxNode); 181 182 resultx = OH_ArkUI_RunTaskInScope((ArkUI_ContextHandle)111, nullptr, nullptr); 183 auto *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>( 184 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")); 185 if (nodeAPI != nullptr) { 186 if (nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) { 187 ArkUI_NodeHandle testNode; 188 testNode = testRenderNode(nodeAPI); 189 190 OH_NativeXComponent_AttachNativeRootNode(component, testNode); 191 } 192 } 193 return nullptr; 194 } 195 196 ``` 197 198 199## 自定义绘制及动画 200 201以下示例创建了一个渲染节点,调用自定义绘制能力并附加动画功能。 202 2031. 按照[接入ArkTS页面](ndk-access-the-arkts-page.md)创建前置工程。 204 2052. 创建渲染节点能力对象。 206 ```c 207 // manager.cpp 208 // 自定义容器组件示例。 209 #include <arkui/native_animate.h> 210 #include <arkui/native_render.h> 211 #include <arkui/native_type.h> 212 #include <arkui/native_node_napi.h> 213 #include <bits/alltypes.h> 214 215 #include <string> 216 217 #include <arkui/native_interface.h> 218 #include <arkui/native_node.h> 219 #include <native_drawing/drawing_canvas.h> 220 #include <native_drawing/drawing_color.h> 221 #include <native_drawing/drawing_path.h> 222 #include <native_drawing/drawing_pen.h> 223 224 ArkUI_NodeHandle testRenderNode2(ArkUI_NativeNodeAPI_1 *nodeAPI, ArkUI_ContextHandle context) { 225 226 ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_COLUMN); 227 ArkUI_NumberValue valueWidth[] = {400}; 228 ArkUI_AttributeItem itemWidth = {valueWidth, sizeof(valueWidth) / sizeof(ArkUI_NumberValue)}; 229 nodeAPI->setAttribute(scroll, NODE_WIDTH, &itemWidth); 230 ArkUI_NumberValue valueHeight[] = {600}; 231 ArkUI_AttributeItem itemHeight = {valueHeight, sizeof(valueHeight) / sizeof(ArkUI_NumberValue)}; 232 nodeAPI->setAttribute(scroll, NODE_HEIGHT, &itemHeight); 233 valueHeight[0].u32 = 0xff00F100; 234 nodeAPI->setAttribute(scroll, NODE_BACKGROUND_COLOR, &itemHeight); 235 236 ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); 237 ArkUI_NodeHandle text = nodeAPI->createNode(ARKUI_NODE_TEXT); 238 ArkUI_AttributeItem content = {.string = "这是C-API页面"}; 239 240 nodeAPI->setAttribute(text, NODE_TEXT_CONTENT, &content); 241 242 ArkUI_NodeHandle Custom = nodeAPI->createNode(ARKUI_NODE_CUSTOM); 243 auto renderNode = OH_ArkUI_RenderNodeUtils_CreateNode(); 244 OH_ArkUI_RenderNodeUtils_AddRenderNode(Custom, renderNode); 245 OH_ArkUI_RenderNodeUtils_SetSize(renderNode, 1000, 1000); 246 247 // Property的作用是触发set更新,同步更新modifier的Draw方法。 248 struct AnimatableUserData { 249 ArkUI_FloatAnimatablePropertyHandle width; 250 ArkUI_FloatAnimatablePropertyHandle height; 251 ArkUI_Vector2AnimatablePropertyHandle v2; 252 ArkUI_ColorAnimatablePropertyHandle color; 253 }; 254 255 // 设置基础值。 256 AnimatableUserData *userData1 = new AnimatableUserData; 257 auto widthAnimProperty = OH_ArkUI_RenderNodeUtils_CreateFloatAnimatableProperty(1000); 258 userData1->width = widthAnimProperty; 259 auto heightAnimProperty = OH_ArkUI_RenderNodeUtils_CreateFloatAnimatableProperty(1000); 260 userData1->height = heightAnimProperty; 261 auto vectorAnimP = OH_ArkUI_RenderNodeUtils_CreateVector2AnimatableProperty(1000, 1000); 262 userData1->v2 = vectorAnimP; 263 auto colorAnimP = OH_ArkUI_RenderNodeUtils_CreateColorAnimatableProperty(0xFFFF11FF); 264 userData1->color = colorAnimP; 265 266 // 关联组件和多个modifier。 267 auto animModifier = OH_ArkUI_RenderNodeUtils_CreateContentModifier(); 268 OH_ArkUI_RenderNodeUtils_AttachContentModifier(renderNode, animModifier); 269 // 关联modifier和property。 270 OH_ArkUI_RenderNodeUtils_AttachFloatAnimatableProperty(animModifier, widthAnimProperty); 271 OH_ArkUI_RenderNodeUtils_AttachFloatAnimatableProperty(animModifier, heightAnimProperty); 272 OH_ArkUI_RenderNodeUtils_AttachVector2AnimatableProperty(animModifier, vectorAnimP); 273 OH_ArkUI_RenderNodeUtils_AttachColorAnimatableProperty(animModifier, colorAnimP); 274 275 // 设置自定义绘制内容。 276 OH_ArkUI_RenderNodeUtils_SetContentModifierOnDraw( 277 animModifier, userData1, [](ArkUI_DrawContext *context, void *userData) { 278 AnimatableUserData *data = (AnimatableUserData *)userData; 279 float width = 0; 280 float height = 0; 281 uint32_t color = 0; 282 ArkUI_Vector2AnimatablePropertyHandle v2 = data->v2; 283 // property主要为传值用,这里用x,y来替代width,实际使用时可以通过property来自定义所需参数。 284 OH_ArkUI_RenderNodeUtils_GetVector2AnimatablePropertyValue(v2, &width, &height); 285 ArkUI_ColorAnimatablePropertyHandle cp = data->color; 286 OH_ArkUI_RenderNodeUtils_GetColorAnimatablePropertyValue(cp, &color); 287 288 289 auto *canvas1 = OH_ArkUI_DrawContext_GetCanvas(context); 290 OH_Drawing_Canvas *canvas = reinterpret_cast<OH_Drawing_Canvas *>(canvas1); 291 auto path = OH_Drawing_PathCreate(); 292 OH_Drawing_PathMoveTo(path, width / 4, height / 4); 293 OH_Drawing_PathLineTo(path, width * 3 / 4, height / 4); 294 OH_Drawing_PathLineTo(path, width * 3 / 4, height * 3 / 4); 295 OH_Drawing_PathLineTo(path, width / 4, height * 3 / 4); 296 OH_Drawing_PathLineTo(path, width / 4, height / 4); 297 OH_Drawing_PathClose(path); 298 auto pen = OH_Drawing_PenCreate(); 299 OH_Drawing_PenSetWidth(pen, 10); 300 OH_Drawing_PenSetColor(pen, color); 301 OH_Drawing_CanvasAttachPen(canvas, pen); 302 OH_Drawing_CanvasDrawPath(canvas, path); 303 }); 304 305 // 用户自定义参数。 306 ArkUI_ContextCallback *update = new ArkUI_ContextCallback; 307 update->userData = userData1; 308 update->callback = [](void *user) { 309 AnimatableUserData *data = (AnimatableUserData *)user; 310 OH_ArkUI_RenderNodeUtils_SetFloatAnimatablePropertyValue(data->width, 100); 311 OH_ArkUI_RenderNodeUtils_SetFloatAnimatablePropertyValue(data->height, 100); 312 OH_ArkUI_RenderNodeUtils_SetVector2AnimatablePropertyValue(data->v2, 100, 100); 313 OH_ArkUI_RenderNodeUtils_SetColorAnimatablePropertyValue(data->color, 0xFF0011FF); 314 }; 315 // 执行对应的动画。 316 ArkUI_NativeAnimateAPI_1 *animateApi = nullptr; 317 OH_ArkUI_GetModuleInterface(ARKUI_NATIVE_ANIMATE, ArkUI_NativeAnimateAPI_1, animateApi); 318 319 ArkUI_AnimateCompleteCallback *completeCallback = new ArkUI_AnimateCompleteCallback; 320 completeCallback->userData = userData1; 321 completeCallback->type = ARKUI_FINISH_CALLBACK_REMOVED; 322 completeCallback->callback = [](void *userData) { 323 AnimatableUserData *data = (AnimatableUserData *)userData; 324 }; 325 326 ArkUI_AnimateOption *option = OH_ArkUI_AnimateOption_Create(); 327 OH_ArkUI_AnimateOption_SetDuration(option, 2000); 328 OH_ArkUI_AnimateOption_SetTempo(option, 1.1); 329 OH_ArkUI_AnimateOption_SetCurve(option, ARKUI_CURVE_EASE); 330 OH_ArkUI_AnimateOption_SetDelay(option, 20); 331 OH_ArkUI_AnimateOption_SetIterations(option, 1); 332 OH_ArkUI_AnimateOption_SetPlayMode(option, ARKUI_ANIMATION_PLAY_MODE_REVERSE); 333 ArkUI_ExpectedFrameRateRange *range = new ArkUI_ExpectedFrameRateRange; 334 range->min = 10; 335 range->max = 120; 336 range->expected = 60; 337 OH_ArkUI_AnimateOption_SetExpectedFrameRateRange(option, range); 338 animateApi->animateTo(context, option, update, completeCallback); 339 340 341 nodeAPI->setAttribute(Custom, NODE_WIDTH, &itemWidth); 342 nodeAPI->setAttribute(Custom, NODE_HEIGHT, &itemHeight); 343 344 nodeAPI->addChild(column, text); 345 nodeAPI->addChild(column, Custom); 346 nodeAPI->addChild(scroll, column); 347 return scroll; 348 } 349 350 napi_value Manager::CreateNativeNode(napi_env env, napi_callback_info info) { 351 if ((env == nullptr) || (info == nullptr)) { 352 return nullptr; 353 } 354 size_t argCnt = 2; 355 napi_value args[2] = {nullptr}; 356 if (napi_get_cb_info(env, info, &argCnt, args, nullptr, nullptr) != napi_ok) { 357 OH_LOG_Print(LOG_APP, LOG_ERROR, LOG_PRINT_DOMAIN, "Manager", "CreateNativeNode napi_get_cb_info failed"); 358 } 359 360 if (argCnt != 2) { 361 napi_throw_type_error(env, NULL, "Wrong number of arguments"); 362 return nullptr; 363 } 364 napi_valuetype valuetype; 365 if (napi_typeof(env, args[0], &valuetype) != napi_ok) { 366 napi_throw_type_error(env, NULL, "napi_typeof failed"); 367 return nullptr; 368 } 369 370 if (valuetype != napi_string) { 371 napi_throw_type_error(env, NULL, "Wrong type of arguments"); 372 return nullptr; 373 } 374 375 char idStr[OH_XCOMPONENT_ID_LEN_MAX + 1] = {'\0'}; 376 constexpr uint64_t idSize = OH_XCOMPONENT_ID_LEN_MAX + 1; 377 size_t length; 378 if (napi_get_value_string_utf8(env, args[0], idStr, idSize, &length) != napi_ok) { 379 napi_throw_type_error(env, NULL, "napi_get_value_int64 failed"); 380 return nullptr; 381 } 382 383 auto manager = Manager::GetInstance(); 384 if (manager == nullptr) { 385 return nullptr; 386 } 387 388 OH_NativeXComponent *component = manager->GetNativeXComponent(idStr); 389 if (component == nullptr) { 390 return nullptr; 391 } 392 393 ArkUI_NodeHandle xxxNode; 394 auto resultx = OH_ArkUI_NodeUtils_GetNodeHandleByUniqueId(3, &xxxNode); 395 396 resultx = OH_ArkUI_RunTaskInScope((ArkUI_ContextHandle)111, nullptr, nullptr); 397 auto *nodeAPI = reinterpret_cast<ArkUI_NativeNodeAPI_1 *>( 398 OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_NODE, "ArkUI_NativeNodeAPI_1")); 399 if (nodeAPI != nullptr) { 400 if (nodeAPI->createNode != nullptr && nodeAPI->addChild != nullptr) { 401 ArkUI_NodeHandle testNode; 402 // 获取ets测传入的context。 403 ArkUI_ContextHandle context = nullptr; 404 // 通过code 判断是否获取成功。 405 auto code = OH_ArkUI_GetContextFromNapiValue(env, args[1], &context); 406 testNode = testRenderNode2(nodeAPI, context); 407 408 OH_NativeXComponent_AttachNativeRootNode(component, testNode); 409 } 410 } 411 return nullptr; 412 } 413 414 ``` 415