1# 查询和操作自定义节点 2<!--Kit: ArkUI--> 3<!--Subsystem: ArkUI--> 4<!--Owner: @xiang-shouxing--> 5<!--Designer: @xiang-shouxing--> 6<!--Tester: @sally__--> 7<!--Adviser: @HelloCrease--> 8 9NDK提供一系列节点查询、遍历、操作能力,通过使用以下接口,开发者可以高效地访问和操控节点。 10 11以下场景基于[接入ArkTS页面](ndk-access-the-arkts-page.md)章节,创建前置工程。 12 13## 查询节点uniqueId及通过uniqueId获取节点信息 14 15uniqueId是系统分配的唯一标识的节点Id。 16 17从API version 20开始,使用[OH_ArkUI_NodeUtils_GetNodeUniqueId](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getnodeuniqueid)接口,可以获取目标节点的uniqueId。使用[OH_ArkUI_NodeUtils_GetNodeHandleByUniqueId](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getnodehandlebyuniqueid)接口,可以通过uniqueId获取目标节点的指针。 18 19```c++ 20testNode = nodeAPI->createNode(ARKUI_NODE_COLUMN); 21ArkUI_NumberValue value[] = {480}; 22ArkUI_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI_NumberValue)}; 23value[0].f32 = 300; 24nodeAPI->setAttribute(testNode, NODE_WIDTH, &item); 25nodeAPI->setAttribute(testNode, NODE_HEIGHT, &item); 26struct IdList { 27 int32_t id = -1; 28}; 29IdList *idl = new IdList; 30int32_t uid = -1; 31OH_ArkUI_NodeUtils_GetNodeUniqueId(testNode, &uid); 32idl->id = uid; 33auto button = nodeAPI->createNode(ARKUI_NODE_BUTTON); 34value[0].f32 = 50; 35nodeAPI->setAttribute(button, NODE_WIDTH, &item); 36nodeAPI->setAttribute(button, NODE_HEIGHT, &item); 37nodeAPI->addChild(testNode, button); 38nodeAPI->registerNodeEvent(button, NODE_ON_CLICK, 1, idl); 39nodeAPI->registerNodeEventReceiver([](ArkUI_NodeEvent *event) { 40 auto targetId = OH_ArkUI_NodeEvent_GetTargetId(event); 41 if (targetId == 1) { 42 auto idl = (IdList *)OH_ArkUI_NodeEvent_GetUserData(event); 43 ArkUI_NodeHandle Test_Column; 44 auto ec = OH_ArkUI_NodeUtils_GetNodeHandleByUniqueId(idl->id, &Test_Column); 45 if (ec == 0) { 46 OH_LOG_Print(LOG_APP, LOG_WARN, 0xFF00, "Manager","GetNodeHandleByUniqueId success"); 47 } 48 } 49}); 50``` 51## 通过用户id获取节点信息 52 53使用[OH_ArkUI_NodeUtils_GetAttachedNodeHandleById](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getattachednodehandlebyid)接口,可以通过用户设置的id获取目标节点的指针。 54 551. ArkTS侧接入Native组件。 56 ```ts 57 // GetNodeById.ets 58 import nativeNode from 'libentry.so'; 59 import { NodeContent } from '@kit.ArkUI'; 60 61 @Entry 62 @Component 63 struct GetNodeById { 64 private rootSlot = new NodeContent(); 65 66 aboutToAppear(): void { 67 nativeNode.createNativeRoot(this.rootSlot); 68 } 69 70 build() { 71 Scroll() { 72 Column({ space: 15 }) { 73 Column() { 74 ContentSlot(this.rootSlot) 75 } 76 } 77 .width('100%') 78 }.scrollBarColor(Color.Transparent) 79 } 80 } 81 ``` 82 832. 新建`GetNodeByIdExample.h`文件,在其中创建Text节点并设置id属性,通过OH_ArkUI_NodeUtils_GetAttachedNodeHandleById接口拿到节点。 84 ```c 85 // GetNodeByIdExample.h 86 #ifndef MYAPPLICATION_GETNODEBYID_H 87 #define MYAPPLICATION_GETNODEBYID_H 88 89 #include "ArkUINode.h" 90 #include <hilog/log.h> 91 92 namespace NativeModule { 93 94 std::shared_ptr<ArkUIBaseNode> CreateGetNodeByIdExample() { 95 auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); 96 97 // 创建传入事件节点结构体 98 struct A { 99 ArkUI_NodeHandle node; 100 }; 101 A* a = new A; 102 103 // 创建根节点Scroll 104 ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); 105 ArkUI_NumberValue length_value[] = {{.f32 = 480}}; 106 ArkUI_AttributeItem length_item = {length_value, sizeof(length_value) / sizeof(ArkUI_NumberValue)}; 107 nodeAPI->setAttribute(scroll, NODE_WIDTH, &length_item); 108 ArkUI_NumberValue length_value1[] = {{.f32 = 650}}; 109 ArkUI_AttributeItem length_item1 = {length_value1, sizeof(length_value1) / sizeof(ArkUI_NumberValue)}; 110 nodeAPI->setAttribute(scroll, NODE_HEIGHT, &length_item1); 111 ArkUI_AttributeItem scroll_id = {.string = "Scroll_CAPI"}; 112 nodeAPI->setAttribute(scroll, NODE_ID, &scroll_id); 113 114 // 创建Column 115 ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); 116 ArkUI_NumberValue value[] = {480}; 117 ArkUI_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI_NumberValue)}; 118 nodeAPI->setAttribute(column, NODE_WIDTH, &item); 119 ArkUI_NumberValue column_bc[] = {{.u32 = 0xFFF00BB}}; 120 ArkUI_AttributeItem column_item = {column_bc, 1}; 121 nodeAPI->setAttribute(column, NODE_BACKGROUND_COLOR, &column_item); 122 ArkUI_AttributeItem column_id = {.string = "Column_CAPI"}; 123 nodeAPI->setAttribute(column, NODE_ID, &column_id); 124 125 // 创建Text 126 ArkUI_NodeHandle text0 = nodeAPI->createNode(ARKUI_NODE_TEXT); 127 ArkUI_NumberValue text_width[] = {300}; 128 ArkUI_AttributeItem text_item0 = {text_width, sizeof(text_width) / sizeof(ArkUI_NumberValue)}; 129 nodeAPI->setAttribute(text0, NODE_WIDTH, &text_item0); 130 ArkUI_NumberValue text_height[] = {50}; 131 ArkUI_AttributeItem text_item1 = {text_height, sizeof(text_height) / sizeof(ArkUI_NumberValue)}; 132 nodeAPI->setAttribute(text0, NODE_HEIGHT, &text_item1); 133 ArkUI_AttributeItem text_item = {.string = "示例Text节点"}; 134 nodeAPI->setAttribute(text0, NODE_TEXT_CONTENT, &text_item); 135 ArkUI_NumberValue margin[] = {10}; 136 ArkUI_AttributeItem item_margin = {margin, sizeof(margin) / sizeof(ArkUI_NumberValue)}; 137 nodeAPI->setAttribute(text0, NODE_MARGIN, &item_margin); 138 ArkUI_AttributeItem text0_id = {.string = "Text0_CAPI"}; 139 nodeAPI->setAttribute(text0, NODE_ID, &text0_id); 140 a->node = text0; 141 142 // 创建Row 143 ArkUI_NodeHandle row0 = nodeAPI->createNode(ARKUI_NODE_ROW); 144 ArkUI_NumberValue width_value[] = {{.f32=330}}; 145 ArkUI_AttributeItem width_item = {width_value, sizeof(width_value) / sizeof(ArkUI_NumberValue)}; 146 nodeAPI->setAttribute(row0, NODE_WIDTH, &width_item); 147 nodeAPI->setAttribute(row0, NODE_HEIGHT, &text_item1); 148 nodeAPI->setAttribute(row0, NODE_MARGIN, &item_margin); 149 150 // 创建Button 151 ArkUI_NodeHandle bt0 = nodeAPI->createNode(ARKUI_NODE_BUTTON); 152 ArkUI_NumberValue btn_width[] = {150}; 153 ArkUI_AttributeItem btn_item0 = {btn_width, sizeof(btn_width) / sizeof(ArkUI_NumberValue)}; 154 nodeAPI->setAttribute(bt0, NODE_WIDTH, &btn_item0); 155 nodeAPI->setAttribute(bt0, NODE_HEIGHT, &text_item1); 156 nodeAPI->setAttribute(bt0, NODE_MARGIN, &item_margin); 157 ArkUI_AttributeItem bt0_item = {.string = "GetAttachedNodeHandleById"}; 158 nodeAPI->setAttribute(bt0, NODE_BUTTON_LABEL, &bt0_item); 159 nodeAPI->registerNodeEvent(bt0, NODE_ON_CLICK, 0, a); 160 161 // 注册事件 162 auto onClick = [](ArkUI_NodeEvent *event) { 163 ArkUI_NodeHandle node = OH_ArkUI_NodeEvent_GetNodeHandle(event); 164 auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); 165 166 if (OH_ArkUI_NodeEvent_GetTargetId(event) == 0) { // GetAttachedNodeHandleById 167 A* a = (A*)OH_ArkUI_NodeEvent_GetUserData(event); 168 ArkUI_NodeHandle node = nullptr; 169 auto res = OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("Text0_CAPI", &node); 170 if (node == a->node) { 171 OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "GetNodeByIdExample", "get Text0_CAPI success"); 172 } else { 173 OH_LOG_Print(LOG_APP, LOG_ERROR, 0xFF00, "GetNodeByIdExample", "get Text0_CAPI failed"); 174 } 175 } 176 }; 177 nodeAPI->registerNodeEventReceiver(onClick); 178 179 // 节点添加 180 nodeAPI->addChild(scroll, column); 181 nodeAPI->addChild(column, text0); 182 nodeAPI->addChild(column, row0); 183 nodeAPI->addChild(row0, bt0); 184 185 return std::make_shared<ArkUINode>(scroll); 186 } 187 } // namespace NativeModule 188 189 #endif //MYAPPLICATION_GETNODEBYID_H 190 ``` 191 1923. 在`NativeEntry.cpp`中,挂载Native节点。 193 ```c 194 // NativeEntry.cpp 195 196 197 #include <arkui/native_node_napi.h> 198 #include <hilog/log.h> 199 #include <js_native_api.h> 200 #include "NativeEntry.h" 201 #include "GetNodeByIdExample.h" 202 203 204 namespace NativeModule { 205 206 207 napi_value CreateNativeRoot(napi_env env, napi_callback_info info) { 208 size_t argc = 1; 209 napi_value args[1] = {nullptr}; 210 211 212 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 213 214 215 // 获取NodeContent 216 ArkUI_NodeContentHandle contentHandle; 217 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); 218 NativeEntry::GetInstance()->SetContentHandle(contentHandle); 219 220 221 // 创建节点 222 auto node = CreateGetNodeByIdExample(); 223 224 225 // 保持Native侧对象到管理类中,维护生命周期。 226 NativeEntry::GetInstance()->SetRootNode(node); 227 return nullptr; 228 } 229 230 231 napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { 232 // 从管理类中释放Native侧对象。 233 NativeEntry::GetInstance()->DisposeRootNode(); 234 return nullptr; 235 } 236 237 238 } // namespace NativeModule 239 ``` 240 2414. 运行程序,点击按钮,打印节点获取成功信息。 242 243## 移动节点 244 245使用[OH_ArkUI_NodeUtils_MoveTo](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_moveto)接口,可以将Native节点移动到新的父节点下,从而按需改变节点树结构。 246 247> **说明:** 248> 249> 当前仅支持以下类型的[ArkUI_NodeType](../reference/apis-arkui/capi-native-node-h.md#arkui_nodetype)进行移动操作:ARKUI_NODE_STACK、ARKUI_NODE_XCOMPONENT、ARKUI_NODE_EMBEDDED_COMPONENT。对于其他类型的节点,移动操作不会生效。 250 2511. ArkTS侧接入Native组件。 252 ```ts 253 // MoveTo.ets 254 import nativeNode from 'libentry.so'; 255 import { NodeContent } from '@kit.ArkUI'; 256 257 @Entry 258 @Component 259 struct MoveTo { 260 private rootSlot = new NodeContent(); 261 262 aboutToAppear(): void { 263 nativeNode.createNativeRoot(this.rootSlot); 264 } 265 266 build() { 267 Scroll() { 268 Column({ space: 15 }) { 269 Column() { 270 ContentSlot(this.rootSlot) 271 } 272 } 273 .width('100%') 274 }.scrollBarColor(Color.Transparent) 275 } 276 } 277 ``` 278 2792. 新建`MoveTo.h`文件,在其中创建Stack节点,通过OH_ArkUI_NodeUtils_MoveTo接口移动Stack节点。 280 ```c 281 // MoveToExample.h 282 #ifndef MYAPPLICATION_MOVETO_H 283 #define MYAPPLICATION_MOVETO_H 284 285 #include "ArkUINode.h" 286 #include <hilog/log.h> 287 288 namespace NativeModule { 289 290 std::shared_ptr<ArkUIBaseNode> CreateMoveToExample() { 291 auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); 292 293 // 创建传入事件节点结构体 294 struct A { 295 ArkUI_NodeHandle node; 296 ArkUI_NodeHandle targetParent; 297 }; 298 A* a = new A; 299 300 // 创建根节点Scroll 301 ArkUI_NodeHandle scroll = nodeAPI->createNode(ARKUI_NODE_SCROLL); 302 ArkUI_NumberValue length_value[] = {{.f32 = 480}}; 303 ArkUI_AttributeItem length_item = {length_value, sizeof(length_value) / sizeof(ArkUI_NumberValue)}; 304 nodeAPI->setAttribute(scroll, NODE_WIDTH, &length_item); 305 ArkUI_NumberValue length_value1[] = {{.f32 = 650}}; 306 ArkUI_AttributeItem length_item1 = {length_value1, sizeof(length_value1) / sizeof(ArkUI_NumberValue)}; 307 nodeAPI->setAttribute(scroll, NODE_HEIGHT, &length_item1); 308 ArkUI_AttributeItem scroll_id = {.string = "Scroll_CAPI"}; 309 nodeAPI->setAttribute(scroll, NODE_ID, &scroll_id); 310 311 // 创建Column 312 ArkUI_NodeHandle column = nodeAPI->createNode(ARKUI_NODE_COLUMN); 313 ArkUI_NumberValue value[] = {480}; 314 ArkUI_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI_NumberValue)}; 315 nodeAPI->setAttribute(column, NODE_WIDTH, &item); 316 ArkUI_AttributeItem column_id = {.string = "Column_CAPI"}; 317 nodeAPI->setAttribute(column, NODE_ID, &column_id); 318 319 // 创建Row 320 ArkUI_NumberValue text_height[] = {50}; 321 ArkUI_AttributeItem text_item1 = {text_height, sizeof(text_height) / sizeof(ArkUI_NumberValue)}; 322 ArkUI_NumberValue margin[] = {10}; 323 ArkUI_AttributeItem item_margin = {margin, sizeof(margin) / sizeof(ArkUI_NumberValue)}; 324 ArkUI_NodeHandle row0 = nodeAPI->createNode(ARKUI_NODE_ROW); 325 ArkUI_NumberValue width_value[] = {{.f32=330}}; 326 ArkUI_AttributeItem width_item = {width_value, sizeof(width_value) / sizeof(ArkUI_NumberValue)}; 327 nodeAPI->setAttribute(row0, NODE_WIDTH, &width_item); 328 nodeAPI->setAttribute(row0, NODE_HEIGHT, &text_item1); 329 nodeAPI->setAttribute(row0, NODE_MARGIN, &item_margin); 330 331 ArkUI_NodeHandle row1 = nodeAPI->createNode(ARKUI_NODE_ROW); 332 nodeAPI->setAttribute(row1, NODE_WIDTH, &width_item); 333 nodeAPI->setAttribute(row1, NODE_HEIGHT, &text_item1); 334 nodeAPI->setAttribute(row1, NODE_MARGIN, &item_margin); 335 a->targetParent = row1; 336 337 ArkUI_NodeHandle row2 = nodeAPI->createNode(ARKUI_NODE_ROW); 338 nodeAPI->setAttribute(row2, NODE_WIDTH, &width_item); 339 nodeAPI->setAttribute(row2, NODE_HEIGHT, &text_item1); 340 nodeAPI->setAttribute(row2, NODE_MARGIN, &item_margin); 341 342 // 创建Stack 343 ArkUI_NodeHandle stack0 = nodeAPI->createNode(ARKUI_NODE_STACK); 344 ArkUI_NumberValue stack_value[] = {{.f32=50}}; 345 ArkUI_AttributeItem stack_item1 = {stack_value, sizeof(width_value) / sizeof(ArkUI_NumberValue)}; 346 nodeAPI->setAttribute(stack0, NODE_WIDTH, &stack_item1); 347 nodeAPI->setAttribute(stack0, NODE_HEIGHT, &stack_item1); 348 ArkUI_NumberValue stack_bc[] = {{.u32 = 0xFFFFB6C1}}; 349 ArkUI_AttributeItem stack_item2 = {stack_bc, 1}; 350 nodeAPI->setAttribute(stack0, NODE_BACKGROUND_COLOR, &stack_item2); 351 a->node = stack0; 352 353 ArkUI_NodeHandle stack1 = nodeAPI->createNode(ARKUI_NODE_STACK); 354 nodeAPI->setAttribute(stack1, NODE_WIDTH, &stack_item1); 355 nodeAPI->setAttribute(stack1, NODE_HEIGHT, &stack_item1); 356 ArkUI_NumberValue stack_bc1[] = {{.u32 = 0xFF6495ED}}; 357 ArkUI_AttributeItem stack_item3 = {stack_bc1, 1}; 358 nodeAPI->setAttribute(stack1, NODE_BACKGROUND_COLOR, &stack_item3); 359 360 ArkUI_NodeHandle stack2 = nodeAPI->createNode(ARKUI_NODE_STACK); 361 nodeAPI->setAttribute(stack2, NODE_WIDTH, &stack_item1); 362 nodeAPI->setAttribute(stack2, NODE_HEIGHT, &stack_item1); 363 ArkUI_NumberValue stack_bc2[] = {{.u32 = 0xFF90EE90}}; 364 ArkUI_AttributeItem stack_item4 = {stack_bc2, 1}; 365 nodeAPI->setAttribute(stack2, NODE_BACKGROUND_COLOR, &stack_item4); 366 367 ArkUI_NodeHandle stack3 = nodeAPI->createNode(ARKUI_NODE_STACK); 368 nodeAPI->setAttribute(stack3, NODE_WIDTH, &stack_item1); 369 nodeAPI->setAttribute(stack3, NODE_HEIGHT, &stack_item1); 370 nodeAPI->setAttribute(stack3, NODE_BACKGROUND_COLOR, &stack_item2); 371 372 ArkUI_NodeHandle stack4 = nodeAPI->createNode(ARKUI_NODE_STACK); 373 nodeAPI->setAttribute(stack4, NODE_WIDTH, &stack_item1); 374 nodeAPI->setAttribute(stack4, NODE_HEIGHT, &stack_item1); 375 nodeAPI->setAttribute(stack4, NODE_BACKGROUND_COLOR, &stack_item3); 376 377 ArkUI_NodeHandle stack5 = nodeAPI->createNode(ARKUI_NODE_STACK); 378 nodeAPI->setAttribute(stack5, NODE_WIDTH, &stack_item1); 379 nodeAPI->setAttribute(stack5, NODE_HEIGHT, &stack_item1); 380 nodeAPI->setAttribute(stack5, NODE_BACKGROUND_COLOR, &stack_item4); 381 382 // 创建Button 383 ArkUI_NodeHandle bt0 = nodeAPI->createNode(ARKUI_NODE_BUTTON); 384 ArkUI_NumberValue btn_width[] = {150}; 385 ArkUI_AttributeItem btn_item0 = {btn_width, sizeof(btn_width) / sizeof(ArkUI_NumberValue)}; 386 nodeAPI->setAttribute(bt0, NODE_WIDTH, &btn_item0); 387 nodeAPI->setAttribute(bt0, NODE_HEIGHT, &text_item1); 388 nodeAPI->setAttribute(bt0, NODE_MARGIN, &item_margin); 389 ArkUI_AttributeItem bt0_item = {.string = "MoveTo"}; 390 nodeAPI->setAttribute(bt0, NODE_BUTTON_LABEL, &bt0_item); 391 nodeAPI->registerNodeEvent(bt0, NODE_ON_CLICK, 0, a); 392 393 // 注册事件 394 auto onClick = [](ArkUI_NodeEvent *event) { 395 ArkUI_NodeHandle node = OH_ArkUI_NodeEvent_GetNodeHandle(event); 396 auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI(); 397 398 if (OH_ArkUI_NodeEvent_GetTargetId(event) == 0) { // MoveTo 399 A* a = (A*)OH_ArkUI_NodeEvent_GetUserData(event); 400 auto res = OH_ArkUI_NodeUtils_MoveTo(a->node, a->targetParent, 2); 401 } 402 }; 403 nodeAPI->registerNodeEventReceiver(onClick); 404 405 // 节点添加 406 nodeAPI->addChild(scroll, column); 407 nodeAPI->addChild(column, row0); 408 nodeAPI->addChild(column, row1); 409 nodeAPI->addChild(column, row2); 410 nodeAPI->addChild(row0, stack0); 411 nodeAPI->addChild(row0, stack1); 412 nodeAPI->addChild(row0, stack2); 413 nodeAPI->addChild(row1, stack3); 414 nodeAPI->addChild(row1, stack4); 415 nodeAPI->addChild(row1, stack5); 416 nodeAPI->addChild(row2, bt0); 417 418 return std::make_shared<ArkUINode>(scroll); 419 } 420 } // namespace NativeModule 421 422 #endif //MYAPPLICATION_MOVETO_H 423 ``` 424 4253. 在`NativeEntry.cpp`中,挂载Native节点。 426 ```c 427 // NativeEntry.cpp 428 429 430 #include <arkui/native_node_napi.h> 431 #include <hilog/log.h> 432 #include <js_native_api.h> 433 #include "NativeEntry.h" 434 #include "MoveToExample.h" 435 436 437 namespace NativeModule { 438 439 440 napi_value CreateNativeRoot(napi_env env, napi_callback_info info) { 441 size_t argc = 1; 442 napi_value args[1] = {nullptr}; 443 444 445 napi_get_cb_info(env, info, &argc, args, nullptr, nullptr); 446 447 448 // 获取NodeContent 449 ArkUI_NodeContentHandle contentHandle; 450 OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle); 451 NativeEntry::GetInstance()->SetContentHandle(contentHandle); 452 453 454 // 创建节点 455 auto node = CreateMoveToExample(); 456 457 458 // 保持Native侧对象到管理类中,维护生命周期。 459 NativeEntry::GetInstance()->SetRootNode(node); 460 return nullptr; 461 } 462 463 464 napi_value DestroyNativeRoot(napi_env env, napi_callback_info info) { 465 // 从管理类中释放Native侧对象。 466 NativeEntry::GetInstance()->DisposeRootNode(); 467 return nullptr; 468 } 469 470 471 } // namespace NativeModule 472 ``` 473 4744. 运行程序,点击按钮,Stack节点会移动到目标位置。 475 476 477 478## 用不同的展开模式获取对应下标的子节点 479 480NDK支持通过不同的展开方式获取目标节点下的有效节点信息。例如,在LazyForEach场景下,可以处理存在多个子节点的情况。 481 482从API version 20开始,使用[OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getfirstchildindexwithoutexpand)接口,可以获取目标节点的第一个存在于组件树的节点。使用[OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getlastchildindexwithoutexpand)接口,可以获取目标节点的最后一个存在于组件树的节点。[OH_ArkUI_NodeUtils_GetChildWithExpandMode](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getchildwithexpandmode)接口,可以通过不同的节点展开模式获取对应下标的子节点。 483 484> **说明:** 485> 486> 节点展开方式请参考[ArkUI_ExpandMode](../reference/apis-arkui/capi-native-type-h.md#arkui_expandmode),此处推荐使用ARKUI_LAZY_EXPAND懒展开方式,智能识别对应场景。 487 4881. 通过ArkTS构造LazyForEach及ArkTS的下树节点展开场景。 489 490 ```ts 491 import { NodeController, FrameNode, UIContext, BuilderNode, ExpandMode, LengthUnit } from '@kit.ArkUI'; 492 493 const TEST_TAG: string = "FrameNode "; 494 495 // BasicDataSource实现了IDataSource接口,用于管理listener监听,以及通知LazyForEach数据更新 496 class BasicDataSource implements IDataSource { 497 private listeners: DataChangeListener[] = []; 498 private originDataArray: string[] = []; 499 500 public totalCount(): number { 501 return 0; 502 } 503 504 public getData(index: number): string { 505 return this.originDataArray[index]; 506 } 507 508 // 该方法为框架侧调用,为LazyForEach组件向其数据源处添加listener监听 509 registerDataChangeListener(listener: DataChangeListener): void { 510 if (this.listeners.indexOf(listener) < 0) { 511 console.info('add listener'); 512 this.listeners.push(listener); 513 } 514 } 515 516 // 该方法为框架侧调用,为对应的LazyForEach组件在数据源处去除listener监听 517 unregisterDataChangeListener(listener: DataChangeListener): void { 518 const pos = this.listeners.indexOf(listener); 519 if (pos >= 0) { 520 console.info('remove listener'); 521 this.listeners.splice(pos, 1); 522 } 523 } 524 525 // 通知LazyForEach组件需要重载所有子组件 526 notifyDataReload(): void { 527 this.listeners.forEach(listener => { 528 listener.onDataReloaded(); 529 }) 530 } 531 532 // 通知LazyForEach组件需要在index对应索引处添加子组件 533 notifyDataAdd(index: number): void { 534 this.listeners.forEach(listener => { 535 listener.onDataAdd(index); 536 // 写法2:listener.onDatasetChange([{type: DataOperationType.ADD, index: index}]); 537 }) 538 } 539 540 // 通知LazyForEach组件在index对应索引处数据有变化,需要重建该子组件 541 notifyDataChange(index: number): void { 542 this.listeners.forEach(listener => { 543 listener.onDataChange(index); 544 // 写法2:listener.onDatasetChange([{type: DataOperationType.CHANGE, index: index}]); 545 }) 546 } 547 548 // 通知LazyForEach组件需要在index对应索引处删除该子组件 549 notifyDataDelete(index: number): void { 550 this.listeners.forEach(listener => { 551 listener.onDataDelete(index); 552 // 写法2:listener.onDatasetChange([{type: DataOperationType.DELETE, index: index}]); 553 }) 554 } 555 556 // 通知LazyForEach组件将from索引和to索引处的子组件进行交换 557 notifyDataMove(from: number, to: number): void { 558 this.listeners.forEach(listener => { 559 listener.onDataMove(from, to); 560 // 写法2:listener.onDatasetChange( 561 // [{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}]); 562 }) 563 } 564 565 notifyDatasetChange(operations: DataOperation[]): void { 566 this.listeners.forEach(listener => { 567 listener.onDatasetChange(operations); 568 }) 569 } 570 } 571 572 class MyDataSource extends BasicDataSource { 573 private dataArray: string[] = [] 574 575 public totalCount(): number { 576 return this.dataArray.length; 577 } 578 579 public getData(index: number): string { 580 return this.dataArray[index]; 581 } 582 583 public addData(index: number, data: string): void { 584 this.dataArray.splice(index, 0, data); 585 this.notifyDataAdd(index); 586 } 587 588 public pushData(data: string): void { 589 this.dataArray.push(data); 590 this.notifyDataAdd(this.dataArray.length - 1); 591 } 592 } 593 594 class Params { 595 data: MyDataSource | null = null; 596 scroller: Scroller | null = null; 597 constructor(data: MyDataSource, scroller: Scroller) { 598 this.data = data; 599 this.scroller = scroller; 600 } 601 } 602 603 @Builder 604 function buildData(params: Params) { 605 List({ scroller: params.scroller }) { 606 LazyForEach(params.data, (item: string) => { 607 ListItem() { 608 Column() { 609 Text(item) 610 .fontSize(20) 611 .onAppear(() => { 612 console.info(TEST_TAG + " node appear: " + item) 613 }) 614 .backgroundColor(Color.Pink) 615 .margin({ 616 top: 30, 617 bottom: 30, 618 left: 10, 619 right: 10 620 }) 621 } 622 } 623 .id(item) 624 }, (item: string) => item) 625 } 626 .cachedCount(5) 627 .listDirection(Axis.Horizontal) 628 } 629 630 class MyNodeController extends NodeController { 631 private rootNode: FrameNode | null = null; 632 private uiContext: UIContext | null = null; 633 private data: MyDataSource = new MyDataSource(); 634 private scroller: Scroller = new Scroller(); 635 636 makeNode(uiContext: UIContext): FrameNode | null { 637 this.uiContext = uiContext; 638 for (let i = 0; i <= 20; i++) { 639 this.data.pushData(`N${i}`); 640 } 641 const params: Params = new Params(this.data, this.scroller); 642 const dataNode: BuilderNode<[Params]> = new BuilderNode(uiContext); 643 dataNode.build(wrapBuilder<[Params]>(buildData), params); 644 this.rootNode = dataNode.getFrameNode(); 645 const scrollToIndexOptions: ScrollToIndexOptions = { 646 extraOffset: { 647 value: 20, unit: LengthUnit.VP 648 } 649 }; 650 this.scroller.scrollToIndex(6, true, ScrollAlign.START, scrollToIndexOptions); 651 return this.rootNode; 652 } 653 654 // 获取不展开场景下第一个活跃节点的下标 655 getFirstChildIndexWithoutExpand() { 656 console.info(`${TEST_TAG} getFirstChildIndexWithoutExpand: ${this.rootNode!.getFirstChildIndexWithoutExpand()}`); 657 } 658 659 // 获取不展开场景下最后一个活跃节点的下标 660 getLastChildIndexWithoutExpand() { 661 console.info(`${TEST_TAG} getLastChildIndexWithoutExpand: ${this.rootNode!.getLastChildIndexWithoutExpand()}`); 662 } 663 664 // 用不展开的方式获取节点 665 getChildWithNotExpand() { 666 const childNode = this.rootNode!.getChild(3, ExpandMode.NOT_EXPAND); 667 console.info(TEST_TAG + " getChild(3, ExpandMode.NOT_EXPAND): " + childNode?.getId()); 668 if (childNode?.getId() === "N9") { 669 console.info(TEST_TAG + " getChild(3, ExpandMode.NOT_EXPAND) result: success."); 670 } else { 671 console.info(TEST_TAG + " getChild(3, ExpandMode.NOT_EXPAND) result: fail."); 672 } 673 } 674 675 // 以展开的方式获取节点 676 getChildWithExpand() { 677 const childNode = this.rootNode!.getChild(3, ExpandMode.EXPAND); 678 console.info(TEST_TAG + " getChild(3, ExpandMode.EXPAND): " + childNode?.getId()); 679 if (childNode?.getId() === "N3") { 680 console.info(TEST_TAG + " getChild(3, ExpandMode.EXPAND) result: success."); 681 } else { 682 console.info(TEST_TAG + " getChild(3, ExpandMode.EXPAND) result: fail."); 683 } 684 } 685 686 getChildWithLazyExpand() { 687 const childNode = this.rootNode!.getChild(3, ExpandMode.LAZY_EXPAND); 688 console.info(TEST_TAG + " getChild(3, ExpandMode.LAZY_EXPAND): " + childNode?.getId()); 689 if (childNode?.getId() === "N3") { 690 console.info(TEST_TAG + " getChild(3, ExpandMode.LAZY_EXPAND) result: success."); 691 } else { 692 console.info(TEST_TAG + " getChild(3, ExpandMode.LAZY_EXPAND) result: fail."); 693 } 694 } 695 } 696 697 @Entry 698 @Component 699 struct Index { 700 private myNodeController: MyNodeController = new MyNodeController(); 701 private scroller: Scroller = new Scroller(); 702 703 build() { 704 Scroll(this.scroller) { 705 Column({ space: 8 }) { 706 Column() { 707 Text("This is a NodeContainer.") 708 .textAlign(TextAlign.Center) 709 .borderRadius(10) 710 .backgroundColor(0xFFFFFF) 711 .width('100%') 712 .fontSize(16) 713 NodeContainer(this.myNodeController) 714 .borderWidth(1) 715 .width(300) 716 .height(100) 717 } 718 719 Button("getFirstChildIndexWithoutExpand") 720 .width(300) 721 .onClick(() => { 722 this.myNodeController.getFirstChildIndexWithoutExpand(); 723 }) 724 Button("getLastChildIndexWithoutExpand") 725 .width(300) 726 .onClick(() => { 727 this.myNodeController.getLastChildIndexWithoutExpand(); 728 }) 729 Button("getChildWithNotExpand") 730 .width(300) 731 .onClick(() => { 732 this.myNodeController.getChildWithNotExpand(); 733 }) 734 Button("getChildWithExpand") 735 .width(300) 736 .onClick(() => { 737 this.myNodeController.getChildWithExpand(); 738 }) 739 Button("getChildWithLazyExpand") 740 .width(300) 741 .onClick(() => { 742 this.myNodeController.getChildWithLazyExpand(); 743 }) 744 } 745 .width("100%") 746 } 747 .scrollable(ScrollDirection.Vertical) // 滚动方向纵向 748 } 749 } 750 ``` 751 7522. NDK侧通过[OH_ArkUI_NodeUtils_GetAttachedNodeHandleById](../reference/apis-arkui/capi-native-node-h.md#oh_arkui_nodeutils_getattachednodehandlebyid)接口获取ArkTS组件,并通过懒展开模式获取对应的子组件信息。 753 ```c++ 754 ArkUI_NodeHandle childNode = nullptr; 755 OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("N3", &childNode); 756 757 uint32_t index = 0; 758 OH_ArkUI_NodeUtils_GetFirstChildIndexWithoutExpand(childNode, &index); 759 uint32_t index1 = 0; 760 OH_ArkUI_NodeUtils_GetLastChildIndexWithoutExpand(childNode, &index1); 761 ArkUI_NodeHandle child = nullptr; 762 auto result = OH_ArkUI_NodeUtils_GetChildWithExpandMode(childNode, 3, &child, 0); 763 OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager", "firstChildIndex - lastChildIndex == %{public}d -- %{public}d, -- getResult= %{public}d", 764 index, index1, result); 765 ``` 766 7673. 查看日志打印的对应错误码返回是否正确,以此判断是否成功获取到对应子节点。 768