• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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![moveToNativeDemo](figures/moveToNativeDemo.gif)
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