查询和操作自定义节点
==========

NDK提供一系列节点查询、遍历、操作能力，通过使用以下接口，开发者可以高效地访问和操控节点。

以下场景基于[接入ArkTS页面](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ndk-access-the-arkts-page)章节，创建前置工程。

#### 查询节点uniqueId及通过uniqueId获取节点信息

uniqueId是系统分配的唯一标识的节点Id。

从API version 20开始，使用[OH\_ArkUI\_NodeUtils\_GetNodeUniqueId](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_getnodeuniqueid)接口，可以获取目标节点的uniqueId。使用[OH\_ArkUI\_NodeUtils\_GetNodeHandleByUniqueId](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_getnodehandlebyuniqueid)接口，可以通过uniqueId获取目标节点的指针。

ArkUI\_NativeNodeAPI\_1\* nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
ArkUI\_NodeHandle testNode = nodeAPI->createNode(ARKUI\_NODE\_COLUMN);
ArkUI\_NumberValue value\[\] = {VALUE\_1};
ArkUI\_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI\_NumberValue)};
value\[0\].f32 = VALUE\_2;
nodeAPI->setAttribute(testNode, NODE\_WIDTH, &item);
nodeAPI->setAttribute(testNode, NODE\_HEIGHT, &item);
struct IdList {
    int32\_t id = -1;
};
IdList \*idl = new IdList;
int32\_t uid = -1;
OH\_ArkUI\_NodeUtils\_GetNodeUniqueId(testNode, &uid);
idl->id = uid;
auto button = nodeAPI->createNode(ARKUI\_NODE\_BUTTON);
value\[0\].f32 = VALUE\_3;
nodeAPI->setAttribute(button, NODE\_WIDTH, &item);
nodeAPI->setAttribute(button, NODE\_HEIGHT, &item);
nodeAPI->addChild(testNode, button);
nodeAPI->registerNodeEvent(button, NODE\_ON\_CLICK, 1, idl);
OH\_LOG\_Print(LOG\_APP, LOG\_WARN, LOG\_PRINT, "GetNodeUniqueId", "GetNodeHandleByUniqueId success1");
nodeAPI->registerNodeEventReceiver(\[\](ArkUI\_NodeEvent \*event) {
    auto targetId = OH\_ArkUI\_NodeEvent\_GetTargetId(event);
    if (targetId == 1) {
        auto idl = (IdList \*)OH\_ArkUI\_NodeEvent\_GetUserData(event);
        ArkUI\_NodeHandle Test\_Column;
        auto ec = OH\_ArkUI\_NodeUtils\_GetNodeHandleByUniqueId(idl->id, &Test\_Column);
        if (ec == 0) {
            OH\_LOG\_Print(LOG\_APP, LOG\_WARN, LOG\_PRINT, "GetNodeUniqueId", "GetNodeHandleByUniqueId success");
        }
    }
});

#### 通过用户id获取节点信息

使用[OH\_ArkUI\_NodeUtils\_GetAttachedNodeHandleById](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_getattachednodehandlebyid)接口，可以通过用户设置的id获取目标节点的指针。

1.  ArkTS侧接入Native组件。
    
    import nativeNode from 'libentry.so';
    import { NodeContent } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct GetNodeById {
      private rootSlot = new NodeContent();
    
      aboutToAppear(): void {
        nativeNode.createUserIdNode(this.rootSlot);
      }
    
      build() {
        Scroll() {
          Column({ space: 15 }) {
            Column() {
              ContentSlot(this.rootSlot)
            }
          }
          .width('100%')
        }.scrollBarColor(Color.Transparent)
      }
    }
    
2.  新建GetNodeByIdExample.h文件，在其中创建Text节点并设置id属性，通过OH\_ArkUI\_NodeUtils\_GetAttachedNodeHandleById接口拿到节点。
    
    // GetNodeByIdExample.h
    #ifndef MYAPPLICATION\_GETNODEBYID\_H
    #define MYAPPLICATION\_GETNODEBYID\_H
    
    #include "ArkUINode.h"
    #include <hilog/log.h>
    
    namespace NativeModule {
    
    std::shared\_ptr<ArkUIBaseNode> CreateGetNodeByIdExample()
    {
        auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
        
        // 创建传入事件节点结构体
        struct A {
            ArkUI\_NodeHandle node;
        };
        A\* a = new A;
        
        // 创建根节点Scroll
        ArkUI\_NodeHandle scroll = nodeAPI->createNode(ARKUI\_NODE\_SCROLL);
        ArkUI\_NumberValue length\_value\[\] = {{.f32 = 480}};
        ArkUI\_AttributeItem length\_item = {length\_value, sizeof(length\_value) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(scroll, NODE\_WIDTH, &length\_item);
        ArkUI\_NumberValue length\_value1\[\] = {{.f32 = 650}};
        ArkUI\_AttributeItem length\_item1 = {length\_value1, sizeof(length\_value1) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(scroll, NODE\_HEIGHT, &length\_item1);
        ArkUI\_AttributeItem scroll\_id = {.string = "Scroll\_CAPI"};
        nodeAPI->setAttribute(scroll, NODE\_ID, &scroll\_id);
        
        // 创建Column
        ArkUI\_NodeHandle column = nodeAPI->createNode(ARKUI\_NODE\_COLUMN);
        ArkUI\_NumberValue value\[\] = {480};
        ArkUI\_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(column, NODE\_WIDTH, &item);
        ArkUI\_NumberValue column\_bc\[\] = {{.u32 = 0xFFF00BB}};
        ArkUI\_AttributeItem column\_item = {column\_bc, 1};
        nodeAPI->setAttribute(column, NODE\_BACKGROUND\_COLOR, &column\_item);
        ArkUI\_AttributeItem column\_id = {.string = "Column\_CAPI"};
        nodeAPI->setAttribute(column, NODE\_ID, &column\_id);
        
        // 创建Text
        ArkUI\_NodeHandle text0 = nodeAPI->createNode(ARKUI\_NODE\_TEXT);
        ArkUI\_NumberValue text\_width\[\] = {300};
        ArkUI\_AttributeItem text\_item0 = {text\_width, sizeof(text\_width) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(text0, NODE\_WIDTH, &text\_item0);
        ArkUI\_NumberValue text\_height\[\] = {50};
        ArkUI\_AttributeItem text\_item1 = {text\_height, sizeof(text\_height) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(text0, NODE\_HEIGHT, &text\_item1);
        ArkUI\_AttributeItem text\_item = {.string = "示例Text节点"};
        nodeAPI->setAttribute(text0, NODE\_TEXT\_CONTENT, &text\_item);
        ArkUI\_NumberValue margin\[\] = {10};
        ArkUI\_AttributeItem item\_margin = {margin, sizeof(margin) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(text0, NODE\_MARGIN, &item\_margin);
        ArkUI\_AttributeItem text0\_id = {.string = "Text0\_CAPI"};
        nodeAPI->setAttribute(text0, NODE\_ID, &text0\_id);
        a->node = text0;
        
        // 创建Row
        ArkUI\_NodeHandle row0 = nodeAPI->createNode(ARKUI\_NODE\_ROW);
        ArkUI\_NumberValue width\_value\[\] = {{.f32=330}};
        ArkUI\_AttributeItem width\_item = {width\_value, sizeof(width\_value) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(row0, NODE\_WIDTH, &width\_item);
        nodeAPI->setAttribute(row0, NODE\_HEIGHT, &text\_item1);
        nodeAPI->setAttribute(row0, NODE\_MARGIN, &item\_margin);
        
        // 创建Button
        ArkUI\_NodeHandle bt0 = nodeAPI->createNode(ARKUI\_NODE\_BUTTON);
        ArkUI\_NumberValue btn\_width\[\] = {150};
        ArkUI\_AttributeItem btn\_item0 = {btn\_width, sizeof(btn\_width) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(bt0, NODE\_WIDTH, &btn\_item0);
        nodeAPI->setAttribute(bt0, NODE\_HEIGHT, &text\_item1);
        nodeAPI->setAttribute(bt0, NODE\_MARGIN, &item\_margin);
        ArkUI\_AttributeItem bt0\_item = {.string = "GetAttachedNodeHandleById"};
        nodeAPI->setAttribute(bt0, NODE\_BUTTON\_LABEL, &bt0\_item);
        nodeAPI->registerNodeEvent(bt0, NODE\_ON\_CLICK, 0, a);
        
        // 注册事件
        auto onClick = \[\](ArkUI\_NodeEvent \*event) {
            ArkUI\_NodeHandle node = OH\_ArkUI\_NodeEvent\_GetNodeHandle(event);
            auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
            
            if (OH\_ArkUI\_NodeEvent\_GetTargetId(event) == 0) {  // GetAttachedNodeHandleById
                A\* a = (A\*)OH\_ArkUI\_NodeEvent\_GetUserData(event);
                ArkUI\_NodeHandle node = nullptr;
                auto res = OH\_ArkUI\_NodeUtils\_GetAttachedNodeHandleById("Text0\_CAPI", &node);
                if (node == a->node) {
                    OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 0xFF00, "GetNodeByIdExample", "get Text0\_CAPI success");
                } else {
                    OH\_LOG\_Print(LOG\_APP, LOG\_ERROR, 0xFF00, "GetNodeByIdExample", "get Text0\_CAPI failed");
                }
            }
        };
        nodeAPI->registerNodeEventReceiver(onClick);
        
        // 节点添加
        nodeAPI->addChild(scroll, column);
        nodeAPI->addChild(column, text0);
        nodeAPI->addChild(column, row0);
        nodeAPI->addChild(row0, bt0);
        
        return std::make\_shared<ArkUINode>(scroll);
    }
    } // namespace NativeModule
    
    #endif // MYAPPLICATION\_GETNODEBYID\_H
    
3.  在NativeEntry.cpp中，挂载Native节点。
    
    // NativeEntry.cpp
    #include <arkui/native\_node\_napi.h>
    #include <hilog/log.h>
    #include <js\_native\_api.h>
    #include "NativeEntry.h"
    #include "MoveToExample.h"
    #include "GetNodeByIdExample.h"
    
    
    namespace NativeModule {
    // ...
    static napi\_value CreateNativeRoot(napi\_env env, napi\_callback\_info info, const char \*who, MakeNodeFn makeNodeFn)
    {
        size\_t argc = 1;
        napi\_value args\[1\] = {nullptr};
    
        napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    
        // 获取NodeContent
        ArkUI\_NodeContentHandle contentHandle;
        OH\_ArkUI\_GetNodeContentFromNapiValue(env, args\[0\], &contentHandle);
        if (contentHandle == nullptr) {
            OH\_LOG\_Print(LOG\_APP, LOG\_INFO, LOG\_PRINT\_DOMAIN, K\_LOG\_DOMAIN,
                         "%{public}s nodeContentHandle is null", who);
            return nullptr;
        }
        NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    
        // 创建节点
        auto node = makeNodeFn();
    
        // 保持Native侧对象到管理类中，维护生命周期。
        NativeEntry::GetInstance()->SetRootNode(node);
        return nullptr;
    }
    
    napi\_value DestroyNativeRoot(napi\_env env, napi\_callback\_info info)
    {
        // 从管理类中释放Native侧对象。
        NativeEntry::GetInstance()->DisposeRootNode();
        return nullptr;
    }
    // ...
    } // namespace NativeModule
    
4.  运行程序，点击按钮，打印节点获取成功信息。
    

#### 移动节点

使用[OH\_ArkUI\_NodeUtils\_MoveTo](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_moveto)接口，可以将Native节点移动到新的父节点下，从而按需改变节点树结构。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/3c/v3/M4gzlCVDTLqpBPqSWbadiQ/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073317Z&HW-CC-Expire=86400&HW-CC-Sign=E51250FBBC05BFE43B95A193780100DC135FD94DF2BD38B105250AC0116A88D3)

当前仅支持以下类型的[ArkUI\_NodeType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#arkui_nodetype)进行移动操作：ARKUI\_NODE\_STACK、ARKUI\_NODE\_XCOMPONENT、ARKUI\_NODE\_EMBEDDED\_COMPONENT。对于其他类型的节点，移动操作不会生效。

1.  ArkTS侧接入Native组件。
    
    // MoveTo.ets
    import nativeNode from 'libentry.so';
    import { NodeContent } from '@kit.ArkUI';
    
    @Entry
    @Component
    struct MoveTo {
      private rootSlot = new NodeContent();
    
      aboutToAppear(): void {
        nativeNode.createMoveToNode(this.rootSlot);
      }
    
      build() {
        Scroll() {
          Column({ space: 15 }) {
            Column() {
              ContentSlot(this.rootSlot)
            }
          }
          .width('100%')
        }.scrollBarColor(Color.Transparent)
      }
    }
    
2.  新建MoveTo.h文件，在其中创建Stack节点，通过OH\_ArkUI\_NodeUtils\_MoveTo接口移动Stack节点。
    
    // MoveToExample.h
    #ifndef MYAPPLICATION\_MOVETO\_H
    #define MYAPPLICATION\_MOVETO\_H
    
    #include "ArkUINode.h"
    #include <hilog/log.h>
    
    namespace NativeModule {
    
    std::shared\_ptr<ArkUIBaseNode> CreateMoveToExample()
    {
        auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
    
        // 创建传入事件节点结构体
        struct A {
            ArkUI\_NodeHandle node;
            ArkUI\_NodeHandle targetParent;
        };
        A\* a = new A;
    
        // 创建根节点Scroll
        ArkUI\_NodeHandle scroll = nodeAPI->createNode(ARKUI\_NODE\_SCROLL);
        ArkUI\_NumberValue length\_value\[\] = {{.f32 = 480}};
        ArkUI\_AttributeItem length\_item = {length\_value, sizeof(length\_value) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(scroll, NODE\_WIDTH, &length\_item);
        ArkUI\_NumberValue length\_value1\[\] = {{.f32 = 650}};
        ArkUI\_AttributeItem length\_item1 = {length\_value1, sizeof(length\_value1) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(scroll, NODE\_HEIGHT, &length\_item1);
        ArkUI\_AttributeItem scroll\_id = {.string = "Scroll\_CAPI"};
        nodeAPI->setAttribute(scroll, NODE\_ID, &scroll\_id);
    
        // 创建Column
        ArkUI\_NodeHandle column = nodeAPI->createNode(ARKUI\_NODE\_COLUMN);
        ArkUI\_NumberValue value\[\] = {480};
        ArkUI\_AttributeItem item = {value, sizeof(value) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(column, NODE\_WIDTH, &item);
        ArkUI\_AttributeItem column\_id = {.string = "Column\_CAPI"};
        nodeAPI->setAttribute(column, NODE\_ID, &column\_id);
    
        // 创建Row
        ArkUI\_NumberValue text\_height\[\] = {50};
        ArkUI\_AttributeItem text\_item1 = {text\_height, sizeof(text\_height) / sizeof(ArkUI\_NumberValue)};
        ArkUI\_NumberValue margin\[\] = {10};
        ArkUI\_AttributeItem item\_margin = {margin, sizeof(margin) / sizeof(ArkUI\_NumberValue)};
        ArkUI\_NodeHandle row0 = nodeAPI->createNode(ARKUI\_NODE\_ROW);
        ArkUI\_NumberValue width\_value\[\] = {{.f32=330}};
        ArkUI\_AttributeItem width\_item = {width\_value, sizeof(width\_value) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(row0, NODE\_WIDTH, &width\_item);
        nodeAPI->setAttribute(row0, NODE\_HEIGHT, &text\_item1);
        nodeAPI->setAttribute(row0, NODE\_MARGIN, &item\_margin);
    
        ArkUI\_NodeHandle row1 = nodeAPI->createNode(ARKUI\_NODE\_ROW);
        nodeAPI->setAttribute(row1, NODE\_WIDTH, &width\_item);
        nodeAPI->setAttribute(row1, NODE\_HEIGHT, &text\_item1);
        nodeAPI->setAttribute(row1, NODE\_MARGIN, &item\_margin);
        a->targetParent = row1;
    
        ArkUI\_NodeHandle row2 = nodeAPI->createNode(ARKUI\_NODE\_ROW);
        nodeAPI->setAttribute(row2, NODE\_WIDTH, &width\_item);
        nodeAPI->setAttribute(row2, NODE\_HEIGHT, &text\_item1);
        nodeAPI->setAttribute(row2, NODE\_MARGIN, &item\_margin);
    
        // 创建Stack
        ArkUI\_NodeHandle stack0 = nodeAPI->createNode(ARKUI\_NODE\_STACK);
        ArkUI\_NumberValue stack\_value\[\] = {{.f32=50}};
        ArkUI\_AttributeItem stack\_item1 = {stack\_value, sizeof(width\_value) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(stack0, NODE\_WIDTH, &stack\_item1);
        nodeAPI->setAttribute(stack0, NODE\_HEIGHT, &stack\_item1);
        ArkUI\_NumberValue stack\_bc\[\] = {{.u32 = 0xFFFFB6C1}};
        ArkUI\_AttributeItem stack\_item2 = {stack\_bc, 1};
        nodeAPI->setAttribute(stack0, NODE\_BACKGROUND\_COLOR, &stack\_item2);
        a->node = stack0;
    
        ArkUI\_NodeHandle stack1 = nodeAPI->createNode(ARKUI\_NODE\_STACK);
        nodeAPI->setAttribute(stack1, NODE\_WIDTH, &stack\_item1);
        nodeAPI->setAttribute(stack1, NODE\_HEIGHT, &stack\_item1);
        ArkUI\_NumberValue stack\_bc1\[\] = {{.u32 = 0xFF6495ED}};
        ArkUI\_AttributeItem stack\_item3 = {stack\_bc1, 1};
        nodeAPI->setAttribute(stack1, NODE\_BACKGROUND\_COLOR, &stack\_item3);
    
        ArkUI\_NodeHandle stack2 = nodeAPI->createNode(ARKUI\_NODE\_STACK);
        nodeAPI->setAttribute(stack2, NODE\_WIDTH, &stack\_item1);
        nodeAPI->setAttribute(stack2, NODE\_HEIGHT, &stack\_item1);
        ArkUI\_NumberValue stack\_bc2\[\] = {{.u32 = 0xFF90EE90}};
        ArkUI\_AttributeItem stack\_item4 = {stack\_bc2, 1};
        nodeAPI->setAttribute(stack2, NODE\_BACKGROUND\_COLOR, &stack\_item4);
    
        ArkUI\_NodeHandle stack3 = nodeAPI->createNode(ARKUI\_NODE\_STACK);
        nodeAPI->setAttribute(stack3, NODE\_WIDTH, &stack\_item1);
        nodeAPI->setAttribute(stack3, NODE\_HEIGHT, &stack\_item1);
        nodeAPI->setAttribute(stack3, NODE\_BACKGROUND\_COLOR, &stack\_item2);
    
        ArkUI\_NodeHandle stack4 = nodeAPI->createNode(ARKUI\_NODE\_STACK);
        nodeAPI->setAttribute(stack4, NODE\_WIDTH, &stack\_item1);
        nodeAPI->setAttribute(stack4, NODE\_HEIGHT, &stack\_item1);
        nodeAPI->setAttribute(stack4, NODE\_BACKGROUND\_COLOR, &stack\_item3);
        
        ArkUI\_NodeHandle stack5 = nodeAPI->createNode(ARKUI\_NODE\_STACK);
        nodeAPI->setAttribute(stack5, NODE\_WIDTH, &stack\_item1);
        nodeAPI->setAttribute(stack5, NODE\_HEIGHT, &stack\_item1);
        nodeAPI->setAttribute(stack5, NODE\_BACKGROUND\_COLOR, &stack\_item4);
    
        // 创建Button
        ArkUI\_NodeHandle bt0 = nodeAPI->createNode(ARKUI\_NODE\_BUTTON);
        ArkUI\_NumberValue btn\_width\[\] = {150};
        ArkUI\_AttributeItem btn\_item0 = {btn\_width, sizeof(btn\_width) / sizeof(ArkUI\_NumberValue)};
        nodeAPI->setAttribute(bt0, NODE\_WIDTH, &btn\_item0);
        nodeAPI->setAttribute(bt0, NODE\_HEIGHT, &text\_item1);
        nodeAPI->setAttribute(bt0, NODE\_MARGIN, &item\_margin);
        ArkUI\_AttributeItem bt0\_item = {.string = "MoveTo"};
        nodeAPI->setAttribute(bt0, NODE\_BUTTON\_LABEL, &bt0\_item);
        nodeAPI->registerNodeEvent(bt0, NODE\_ON\_CLICK, 0, a);
    
        // 注册事件
        auto onClick = \[\](ArkUI\_NodeEvent \*event) {
            ArkUI\_NodeHandle node = OH\_ArkUI\_NodeEvent\_GetNodeHandle(event);
            auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
            
            if (OH\_ArkUI\_NodeEvent\_GetTargetId(event) == 0) {  // MoveTo
                A\* a = (A\*)OH\_ArkUI\_NodeEvent\_GetUserData(event);
                auto res = OH\_ArkUI\_NodeUtils\_MoveTo(a->node, a->targetParent, 2);
            }
        };
        nodeAPI->registerNodeEventReceiver(onClick);
    
        // 节点添加
        nodeAPI->addChild(scroll, column);
        nodeAPI->addChild(column, row0);
        nodeAPI->addChild(column, row1);
        nodeAPI->addChild(column, row2);
        nodeAPI->addChild(row0, stack0);
        nodeAPI->addChild(row0, stack1);
        nodeAPI->addChild(row0, stack2);
        nodeAPI->addChild(row1, stack3);
        nodeAPI->addChild(row1, stack4);
        nodeAPI->addChild(row1, stack5);
        nodeAPI->addChild(row2, bt0);
    
        return std::make\_shared<ArkUINode>(scroll);
    }
    } // namespace NativeModule
    
    #endif // MYAPPLICATION\_MOVETO\_H
    
3.  在NativeEntry.cpp中，挂载Native节点。
    
    // NativeEntry.cpp
    #include <arkui/native\_node\_napi.h>
    #include <hilog/log.h>
    #include <js\_native\_api.h>
    #include "NativeEntry.h"
    #include "MoveToExample.h"
    #include "GetNodeByIdExample.h"
    
    
    namespace NativeModule {
    // ...
    static napi\_value CreateNativeRoot(napi\_env env, napi\_callback\_info info, const char \*who, MakeNodeFn makeNodeFn)
    {
        size\_t argc = 1;
        napi\_value args\[1\] = {nullptr};
    
        napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    
        // 获取NodeContent
        ArkUI\_NodeContentHandle contentHandle;
        OH\_ArkUI\_GetNodeContentFromNapiValue(env, args\[0\], &contentHandle);
        if (contentHandle == nullptr) {
            OH\_LOG\_Print(LOG\_APP, LOG\_INFO, LOG\_PRINT\_DOMAIN, K\_LOG\_DOMAIN,
                         "%{public}s nodeContentHandle is null", who);
            return nullptr;
        }
        NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    
        // 创建节点
        auto node = makeNodeFn();
    
        // 保持Native侧对象到管理类中，维护生命周期。
        NativeEntry::GetInstance()->SetRootNode(node);
        return nullptr;
    }
    
    napi\_value DestroyNativeRoot(napi\_env env, napi\_callback\_info info)
    {
        // 从管理类中释放Native侧对象。
        NativeEntry::GetInstance()->DisposeRootNode();
        return nullptr;
    }
    // ...
    } // namespace NativeModule
    
4.  运行程序，点击按钮，Stack节点会移动到目标位置。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/4d/v3/qR--UA8dQu2QhTNFDXVQ9w/zh-cn_image_0000002594828200.gif?HW-CC-KV=V1&HW-CC-Date=20260612T073317Z&HW-CC-Expire=86400&HW-CC-Sign=0340865762147C3E172927C162ABAAB540EADA13FF4B26C9B5772B2BDF00B7CA)
    

#### 在当前即时帧触发节点属性更新

从API version 21开始，使用[OH\_ArkUI\_NativeModule\_InvalidateAttributes](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nativemodule_invalidateattributes)接口，在当前帧即时触发节点属性更新，避免组件切换过程中出现闪烁。

1.  ArkTS侧接入Native组件。
    
    import testNapi from 'libentry.so';
    import { NodeContent } from '@kit.ArkUI';
    
    @Component
    struct ImageContent {
      private nodeContent: NodeContent = new NodeContent();
    
      aboutToAppear() {
        // 通过C-API创建节点，并添加到管理器nodeContent上
        testNapi.createNativeNode(this.nodeContent);
      }
      build() {
        Column() {
          // 显示nodeContent管理器里存放的Native侧的组件
          ContentSlot(this.nodeContent)
        }
      }
    }
    
    @Entry
    @Component
    struct Index {
      @State message: string = 'Hello World';
      @State showParent: boolean = true;
      build() {
        Row() {
          Column() {
            // $r('app.string.Switch')需要替换为开发者所需的资源文件。
            Button($r('app.string.Switch')).onClick(()=>{
              this.showParent = !this.showParent;
            }).margin(20)
            if(this.showParent) {
              ImageContent()
            } else {
              ImageContent()
            }
          }
          .width('100%')
        }
        .height('100%')
      }
    }
    
2.  新建Attribute\_util.h用于设置组件属性。
    
    #ifndef MYAPPLICATION\_ATTRIBUTE\_UTIL\_H
    #define MYAPPLICATION\_ATTRIBUTE\_UTIL\_H
    
    #include <arkui/native\_node.h>
    #include <cstdint>
    #include <string>
    class AttributeUtil {
    public:
        ArkUI\_NativeNodeAPI\_1 \*api\_;
        ArkUI\_NodeHandle node\_;
        AttributeUtil(ArkUI\_NodeHandle node, ArkUI\_NativeNodeAPI\_1 \*api)
        {
            this->node\_ = node;
            api\_ = api;
        }
        int32\_t Width(float width)
        {
            ArkUI\_NumberValue NODE\_WIDTH\_value\[\] = {width};
            ArkUI\_AttributeItem NODE\_WIDTH\_Item = {NODE\_WIDTH\_value, 1};
            return api\_->setAttribute(node\_, NODE\_WIDTH, &NODE\_WIDTH\_Item);
        }
        int32\_t Height(float height)
        {
            ArkUI\_NumberValue NODE\_HEIGHT\_value\[\] = {height};
            ArkUI\_AttributeItem NODE\_HEIGHT\_Item = {NODE\_HEIGHT\_value, 1};
            return api\_->setAttribute(node\_, NODE\_HEIGHT, &NODE\_HEIGHT\_Item);
        }
        int32\_t ImageSrc(std::string src)
        {
            ArkUI\_AttributeItem NODE\_IMAGE\_SRC\_VALUE = {.string = src.c\_str()};
            return api\_->setAttribute(node\_, NODE\_IMAGE\_SRC, &NODE\_IMAGE\_SRC\_VALUE);
        }
        int32\_t ImageSyncLoad()
        {
            ArkUI\_NumberValue NODE\_TRANSLATE\_ITEM\_VALUE\[\] = {{.i32 = 1}};
            ArkUI\_AttributeItem NODE\_BORDER\_WIDTH\_ITEM = {NODE\_TRANSLATE\_ITEM\_VALUE, 1};
            return api\_->setAttribute(node\_, NODE\_IMAGE\_SYNC\_LOAD, &NODE\_BORDER\_WIDTH\_ITEM);
        }
    };
    #endif // MYAPPLICATION\_ATTRIBUTE\_UTIL\_H
    
3.  在nai\_init.cpp中，挂载Native节点。
    
    #include "Attribute\_util.h"
    #include "napi/native\_api.h"
    #include <arkui/native\_interface.h>
    #include <arkui/native\_node.h>
    #include <arkui/native\_node\_napi.h>
    #include <hilog/log.h>
    #include <js\_native\_api.h>
    #include <js\_native\_api\_types.h>
    // ...
    const unsigned int NUMBER\_2 = 2;
    const unsigned int NUMBER\_WIDTH = 100;
    const unsigned int NUMBER\_HEIGHT = 100;
    
    static napi\_value Add(napi\_env env, napi\_callback\_info info)
    {
        size\_t argc = NUMBER\_2;
        napi\_value args\[NUMBER\_2\] = {nullptr};
    
        napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
    
        napi\_valuetype valuetype0;
        napi\_typeof(env, args\[0\], &valuetype0);
    
        napi\_valuetype valuetype1;
        napi\_typeof(env, args\[1\], &valuetype1);
    
        double value0;
        napi\_get\_value\_double(env, args\[0\], &value0);
    
        double value1;
        napi\_get\_value\_double(env, args\[1\], &value1);
    
        napi\_value sum;
        napi\_create\_double(env, value0 + value1, &sum);
    
        return sum;
    }
    
    static ArkUI\_NativeNodeAPI\_1 \*nodeAPI = nullptr;
    
    static napi\_value NAPI\_Global\_createNativeNode(napi\_env env, napi\_callback\_info info)
    {
        size\_t argc = 1;
        napi\_value args\[1\] = {nullptr};
        napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
        ArkUI\_NodeContentHandle contentHandle;
        OH\_ArkUI\_GetNodeContentFromNapiValue(env, args\[0\], &contentHandle);
        OH\_ArkUI\_GetModuleInterface(ARKUI\_NATIVE\_NODE, ArkUI\_NativeNodeAPI\_1, nodeAPI);
        // 创建Image组件
        auto imageNode = nodeAPI->createNode(ARKUI\_NODE\_IMAGE);
        AttributeUtil imageNodeAttr(imageNode, nodeAPI);
        // 设置image组件属性
        imageNodeAttr.ImageSrc("resources/base/media/startIcon.png");
        imageNodeAttr.ImageSyncLoad();
        imageNodeAttr.Width(NUMBER\_WIDTH);
        imageNodeAttr.Height(NUMBER\_HEIGHT);
        // 在当前即时帧触发节点属性更新
        OH\_ArkUI\_NativeModule\_InvalidateAttributes(imageNode);
        // 挂载image组件到组件树
        OH\_ArkUI\_NodeContent\_AddNode(contentHandle, imageNode);
        return nullptr;
    }
    
    EXTERN\_C\_START
    static napi\_value Init(napi\_env env, napi\_value exports)
    {
        napi\_property\_descriptor desc\[\] = {
            {"add", nullptr, Add, nullptr, nullptr, nullptr, napi\_default, nullptr},
            {"createNativeNode", nullptr, NAPI\_Global\_createNativeNode, nullptr, nullptr, nullptr, napi\_default, nullptr},
            // ...
        };
        napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
        return exports;
    }
    EXTERN\_C\_END
    
    static napi\_module demoModule = {
        .nm\_version = 1,
        .nm\_flags = 0,
        .nm\_filename = nullptr,
        .nm\_register\_func = Init,
        .nm\_modname = "entry",
        .nm\_priv = ((void\*)0),
        .reserved = { 0 },
    };
    
    extern "C" \_\_attribute\_\_((constructor)) void RegisterEntryModule(void)
    {
        napi\_module\_register(&demoModule);
    }
    
4.  运行程序，点击按钮，切换图片正常展示。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/ea/v3/fQjwHz-QS-WpnnFjOSe6aQ/zh-cn_image_0000002625187647.png?HW-CC-KV=V1&HW-CC-Date=20260612T073317Z&HW-CC-Expire=86400&HW-CC-Sign=160936F95E398D26D6D9CE664B936A70A9594D802063D9B416E4F5465A5E1446)
    

#### 用不同的展开模式获取对应下标的子节点

NDK支持通过不同的展开方式获取目标节点下的有效节点信息。例如，在LazyForEach场景下，可以处理存在多个子节点的情况。

从API version 20开始，使用[OH\_ArkUI\_NodeUtils\_GetFirstChildIndexWithoutExpand](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_getfirstchildindexwithoutexpand)接口，可以获取目标节点的第一个存在于组件树的节点。使用[OH\_ArkUI\_NodeUtils\_GetLastChildIndexWithoutExpand](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_getlastchildindexwithoutexpand)接口，可以获取目标节点的最后一个存在于组件树的节点。[OH\_ArkUI\_NodeUtils\_GetChildWithExpandMode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_getchildwithexpandmode)接口，可以通过不同的节点展开模式获取对应下标的子节点。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/d5/v3/ZbOMJrdkRdOxBbzmeNomVA/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260612T073317Z&HW-CC-Expire=86400&HW-CC-Sign=5F18817AA6203B3BFCD4A601995ECBA56D49FA3663C7C83F3170FA7633302622)

节点展开方式请参考[ArkUI\_ExpandMode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-type-h#arkui_expandmode)，此处推荐使用ARKUI\_LAZY\_EXPAND懒展开方式，智能识别对应场景。

1.  通过ArkTS构造LazyForEach及ArkTS的下树节点展开场景。
    
    import { NodeController, FrameNode, UIContext, BuilderNode, ExpandMode, LengthUnit } from '@kit.ArkUI';
    
    const TEST\_TAG: string = "FrameNode ";
    
    // BasicDataSource实现了IDataSource接口，用于管理listener监听，以及通知LazyForEach数据更新
    class BasicDataSource implements IDataSource {
      private listeners: DataChangeListener\[\] = \[\];
      private originDataArray: string\[\] = \[\];
    
      public totalCount(): number {
        return 0;
      }
    
      public getData(index: number): string {
        return this.originDataArray\[index\];
      }
    
      // 该方法为框架侧调用，为LazyForEach组件向其数据源处添加listener监听
      registerDataChangeListener(listener: DataChangeListener): void {
        if (this.listeners.indexOf(listener) < 0) {
          console.info('add listener');
          this.listeners.push(listener);
        }
      }
    
      // 该方法为框架侧调用，为对应的LazyForEach组件在数据源处去除listener监听
      unregisterDataChangeListener(listener: DataChangeListener): void {
        const pos = this.listeners.indexOf(listener);
        if (pos >= 0) {
          console.info('remove listener');
          this.listeners.splice(pos, 1);
        }
      }
    
      // 通知LazyForEach组件需要重载所有子组件
      notifyDataReload(): void {
        this.listeners.forEach(listener => {
          listener.onDataReloaded();
        })
      }
    
      // 通知LazyForEach组件需要在index对应索引处添加子组件
      notifyDataAdd(index: number): void {
        this.listeners.forEach(listener => {
          listener.onDataAdd(index);
          // 写法2：listener.onDatasetChange(\[{type: DataOperationType.ADD, index: index}\]);
        })
      }
    
      // 通知LazyForEach组件在index对应索引处数据有变化，需要重建该子组件
      notifyDataChange(index: number): void {
        this.listeners.forEach(listener => {
          listener.onDataChange(index);
          // 写法2：listener.onDatasetChange(\[{type: DataOperationType.CHANGE, index: index}\]);
        })
      }
    
      // 通知LazyForEach组件需要在index对应索引处删除该子组件
      notifyDataDelete(index: number): void {
        this.listeners.forEach(listener => {
          listener.onDataDelete(index);
          // 写法2：listener.onDatasetChange(\[{type: DataOperationType.DELETE, index: index}\]);
        })
      }
    
      // 通知LazyForEach组件将from索引和to索引处的子组件进行交换
      notifyDataMove(from: number, to: number): void {
        this.listeners.forEach(listener => {
          listener.onDataMove(from, to);
          // 写法2：listener.onDatasetChange(
          //         \[{type: DataOperationType.EXCHANGE, index: {start: from, end: to}}\]);
        })
      }
    
      notifyDatasetChange(operations: DataOperation\[\]): void {
        this.listeners.forEach(listener => {
          listener.onDatasetChange(operations);
        })
      }
    }
    
    class MyDataSource extends BasicDataSource {
      private dataArray: string\[\] = \[\]
    
      public totalCount(): number {
        return this.dataArray.length;
      }
    
      public getData(index: number): string {
        return this.dataArray\[index\];
      }
    
      public addData(index: number, data: string): void {
        this.dataArray.splice(index, 0, data);
        this.notifyDataAdd(index);
      }
    
      public pushData(data: string): void {
        this.dataArray.push(data);
        this.notifyDataAdd(this.dataArray.length - 1);
      }
    }
    
    class Params {
      data: MyDataSource | null = null;
      scroller: Scroller | null = null;
      constructor(data: MyDataSource, scroller: Scroller) {
        this.data = data;
        this.scroller = scroller;
      }
    }
    
    @Builder
    function buildData(params: Params) {
      List({ scroller: params.scroller }) {
        LazyForEach(params.data, (item: string) => {
          ListItem() {
            Column() {
              Text(item)
                .fontSize(20)
                .onAppear(() => {
                  console.info(TEST\_TAG + " node appear: " + item)
                })
                .backgroundColor(Color.Pink)
                .margin({
                  top: 30,
                  bottom: 30,
                  left: 10,
                  right: 10
                })
            }
          }
          .id(item)
        }, (item: string) => item)
      }
      .cachedCount(5)
      .listDirection(Axis.Horizontal)
    }
    
    class MyNodeController extends NodeController {
      private rootNode: FrameNode | null = null;
      private uiContext: UIContext | null = null;
      private data: MyDataSource = new MyDataSource();
      private scroller: Scroller = new Scroller();
    
      makeNode(uiContext: UIContext): FrameNode | null {
        this.uiContext = uiContext;
        for (let i = 0; i <= 20; i++) {
          this.data.pushData(\`N${i}\`);
        }
        const params: Params = new Params(this.data, this.scroller);
        const dataNode: BuilderNode<\[Params\]> = new BuilderNode(uiContext);
        dataNode.build(wrapBuilder<\[Params\]>(buildData), params);
        this.rootNode = dataNode.getFrameNode();
        const scrollToIndexOptions: ScrollToIndexOptions = {
          extraOffset: {
            value: 20, unit: LengthUnit.VP
          }
        };
        this.scroller.scrollToIndex(6, true, ScrollAlign.START, scrollToIndexOptions);
        return this.rootNode;
      }
    
      // 获取不展开场景下第一个活跃节点的下标
      getFirstChildIndexWithoutExpand() {
        console.info(\`${TEST\_TAG} getFirstChildIndexWithoutExpand: ${this.rootNode!.getFirstChildIndexWithoutExpand()}\`);
      }
    
      // 获取不展开场景下最后一个活跃节点的下标
      getLastChildIndexWithoutExpand() {
        console.info(\`${TEST\_TAG} getLastChildIndexWithoutExpand: ${this.rootNode!.getLastChildIndexWithoutExpand()}\`);
      }
    
      // 用不展开的方式获取节点
      getChildWithNotExpand() {
        const childNode = this.rootNode!.getChild(3, ExpandMode.NOT\_EXPAND);
        console.info(TEST\_TAG + " getChild(3, ExpandMode.NOT\_EXPAND): " + childNode?.getId());
        if (childNode?.getId() === "N9") {
          console.info(TEST\_TAG + " getChild(3, ExpandMode.NOT\_EXPAND)  result: success.");
        } else {
          console.info(TEST\_TAG + " getChild(3, ExpandMode.NOT\_EXPAND)  result: fail.");
        }
      }
      
      // 以展开的方式获取节点
      getChildWithExpand() {
        const childNode = this.rootNode!.getChild(3, ExpandMode.EXPAND);
        console.info(TEST\_TAG + " getChild(3, ExpandMode.EXPAND): " + childNode?.getId());
        if (childNode?.getId() === "N3") {
          console.info(TEST\_TAG + " getChild(3, ExpandMode.EXPAND)  result: success.");
        } else {
          console.info(TEST\_TAG + " getChild(3, ExpandMode.EXPAND)  result: fail.");
        }
      }
      
      getChildWithLazyExpand() {
        const childNode = this.rootNode!.getChild(3, ExpandMode.LAZY\_EXPAND);
        console.info(TEST\_TAG + " getChild(3, ExpandMode.LAZY\_EXPAND): " + childNode?.getId());
        if (childNode?.getId() === "N3") {
          console.info(TEST\_TAG + " getChild(3, ExpandMode.LAZY\_EXPAND)  result: success.");
        } else {
          console.info(TEST\_TAG + " getChild(3, ExpandMode.LAZY\_EXPAND)  result: fail.");
        }
      }
    }
    
    @Entry
    @Component
    struct Index {
      private myNodeController: MyNodeController = new MyNodeController();
      private scroller: Scroller = new Scroller();
    
      build() {
        Scroll(this.scroller) {
          Column({ space: 8 }) {
            Column() {
              Text("This is a NodeContainer.")
                .textAlign(TextAlign.Center)
                .borderRadius(10)
                .backgroundColor(0xFFFFFF)
                .width('100%')
                .fontSize(16)
              NodeContainer(this.myNodeController)
                .borderWidth(1)
                .width(300)
                .height(100)
            }
    
            Button("getFirstChildIndexWithoutExpand")
                .width(300)
                .onClick(() => {
                  this.myNodeController.getFirstChildIndexWithoutExpand();
                })
              Button("getLastChildIndexWithoutExpand")
                .width(300)
                .onClick(() => {
                  this.myNodeController.getLastChildIndexWithoutExpand();
                })
              Button("getChildWithNotExpand")
                .width(300)
                .onClick(() => {
                  this.myNodeController.getChildWithNotExpand();
                })
              Button("getChildWithExpand")
                .width(300)
                .onClick(() => {
                  this.myNodeController.getChildWithExpand();
                })
              Button("getChildWithLazyExpand")
                .width(300)
                .onClick(() => {
                  this.myNodeController.getChildWithLazyExpand();
                })
            }
            .width("100%")
          }
          .scrollable(ScrollDirection.Vertical) // 滚动方向纵向
        }
      }
    
2.  NDK侧通过[OH\_ArkUI\_NodeUtils\_GetAttachedNodeHandleById](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nodeutils_getattachednodehandlebyid)接口获取ArkTS组件，并通过懒展开模式获取对应的子组件信息。
    
    ArkUI\_NodeHandle childNode = nullptr;
    OH\_ArkUI\_NodeUtils\_GetAttachedNodeHandleById("N3", &childNode);
        
    uint32\_t index = 0;
    OH\_ArkUI\_NodeUtils\_GetFirstChildIndexWithoutExpand(childNode, &index);
    uint32\_t index1 = 0;
    OH\_ArkUI\_NodeUtils\_GetLastChildIndexWithoutExpand(childNode, &index1);
    ArkUI\_NodeHandle child = nullptr;
    auto result = OH\_ArkUI\_NodeUtils\_GetChildWithExpandMode(childNode, 3, &child, 0);
    OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 0xFF00, "Manager",
        "firstChildIndex - lastChildIndex == %{d -- %{public}d, -- getResult = %{public}d",
        index, index1, result);
    
3.  查看日志打印的对应错误码返回是否正确，以此判断是否成功获取到对应子节点。
    

#### 节点是否处于渲染状态

从API version 23开始，使用[OH\_ArkUI\_NativeModule\_IsInRenderState](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#oh_arkui_nativemodule_isinrenderstate)接口，可以查询节点是否在渲染树上。

1.  ArkTS侧接入Native组件。
    
    //Index.ets
    
    import testNapi from 'libentry.so';
    import { NodeContent } from '@kit.ArkUI';
    
    @Component
    struct TestContent {
      private nodeContent: NodeContent = new NodeContent();
    
      aboutToAppear() {
        // 通过C-API创建节点，并添加到管理器nodeContent上
        testNapi.createNativeNode(this.nodeContent);
      }
      build() {
        Column() {
          // 显示nodeContent管理器里存放的Native侧的组件
          ContentSlot(this.nodeContent)
        }
      }
    }
    
    @Entry
    @Component
    struct Index {
      @State message: string = 'Hello World';
      @State showParent: boolean = true;
      build() {
        Row() {
          Column() {
            TestContent()
          }
          .width('100%')
        }
        .height('100%')
      }
    }
    
2.  新建Attribute\_util .h用于设置组件属性。
    
    #ifndef MYAPPLICATION\_ATTRIBUTE\_UTIL\_H
    #define MYAPPLICATION\_ATTRIBUTE\_UTIL\_H
    #include <arkui/native\_node.h>
    #include <cstdint>
    #include <string>
      class AttributeUtil {
        public:
        ArkUI\_NativeNodeAPI\_1 \*api\_;
        ArkUI\_NodeHandle node\_;
        AttributeUtil(ArkUI\_NodeHandle node, ArkUI\_NativeNodeAPI\_1 \*api) {
        this->node\_ = node;
        api\_ = api;
      }
    int32\_t width(float width) {
      ArkUI\_NumberValue NODE\_WIDTH\_value\[\] = {width};
      ArkUI\_AttributeItem NODE\_WIDTH\_Item = {NODE\_WIDTH\_value, 1};
      return api\_->setAttribute(node\_, NODE\_WIDTH, &NODE\_WIDTH\_Item);
    }
    int32\_t height(float height) {
      ArkUI\_NumberValue NODE\_HEIGHT\_value\[\] = {height};
      ArkUI\_AttributeItem NODE\_HEIGHT\_Item = {NODE\_HEIGHT\_value, 1};
      return api\_->setAttribute(node\_, NODE\_HEIGHT, &NODE\_HEIGHT\_Item);
    }
    
    int32\_t buttonLabel(std::string text) {
      ArkUI\_AttributeItem NODE\_TRANSLATE\_ITEM\_LABEL = {.string = text.c\_str()};
      return api\_->setAttribute(node\_, NODE\_BUTTON\_LABEL, &NODE\_TRANSLATE\_ITEM\_LABEL);
    }
    
    int32\_t text(std::string str) {
      ArkUI\_AttributeItem TEXT\_ITEM = {.string = str.c\_str()};
      return api\_->setAttribute(node\_, NODE\_TEXT\_CONTENT, &TEXT\_ITEM);
    }
    
    int32\_t visibility(int isSHow) {
      ArkUI\_NumberValue NODE\_VISIBILITY\_ITEM\_VALUE = {.i32 = isSHow};
      ArkUI\_AttributeItem NODE\_VISIBILITY\_\_ITEM = {&NODE\_VISIBILITY\_ITEM\_VALUE, 1};
      return api\_->setAttribute(node\_, NODE\_VISIBILITY, &NODE\_VISIBILITY\_\_ITEM);
    }
    
    int32\_t margin(float value) {
      ArkUI\_NumberValue NODE\_margin\_ITEM\_VALUE = {.f32 = value};
      ArkUI\_AttributeItem NODE\_MARGIN\_ITEM = {&NODE\_margin\_ITEM\_VALUE, 1};
      return api\_->setAttribute(node\_, NODE\_MARGIN, &NODE\_MARGIN\_ITEM);
    }
    };
    
    #endif // MYAPPLICATION\_ATTRIBUTE\_UTIL\_H
    
3.  在nai\_init.cpp中，挂载Native节点。
    
    #include "napi/native\_api.h"
    #include "AttributeUtil.h"
    #include <arkui/native\_interface.h>
    #include <arkui/native\_node.h>
    #include <arkui/native\_node\_napi.h>
    #include <hilog/log.h>
    
    static ArkUI\_NativeNodeAPI\_1 \*nodeAPI = nullptr;
    static ArkUI\_NodeHandle textNode = nullptr;
    static bool showText = false;
    
    namespace Event {
      void onClickFunc(ArkUI\_NodeEvent \*event) {
        AttributeUtil textAttr(textNode, nodeAPI);
        if (showText) {
          textAttr.visibility(0);
        } else {
          textAttr.visibility(1);
        }
        showText = !showText;
        bool isOnRenderTree = false;
        OH\_ArkUI\_NativeModule\_IsInRenderState(textNode, &isOnRenderTree);
        OH\_LOG\_Print(LOG\_APP, LOG\_INFO, 1, "event","on render tree statie is %{public}d", isOnRenderTree);
      }
    } // namespace Event
    
    
    static napi\_value NAPI\_Global\_createNativeNode(napi\_env env, napi\_callback\_info info) {
      size\_t argc = 1;
      napi\_value args\[1\] = {nullptr};
      napi\_get\_cb\_info(env, info, &argc, args, nullptr, nullptr);
      ArkUI\_NodeContentHandle contentHandle;
      OH\_ArkUI\_GetNodeContentFromNapiValue(env, args\[0\], &contentHandle);
      OH\_ArkUI\_GetModuleInterface(ARKUI\_NATIVE\_NODE, ArkUI\_NativeNodeAPI\_1, nodeAPI);
      auto columnTest = nodeAPI->createNode(ARKUI\_NODE\_COLUMN);
      AttributeUtil columnAttr(columnTest, nodeAPI);
      columnAttr.width(300);
      columnAttr.height(300);
      auto buttonNode = nodeAPI->createNode(ARKUI\_NODE\_BUTTON);
      nodeAPI->addChild(columnTest, buttonNode);
      AttributeUtil buttonAttr(buttonNode, nodeAPI);
      buttonAttr.width(200);
      buttonAttr.height(30);
      buttonAttr.margin(20);
      buttonAttr.buttonLabel("change text visibility");
      nodeAPI->registerNodeEvent(buttonNode, NODE\_ON\_CLICK, 1, nullptr);
      nodeAPI->registerNodeEventReceiver(Event::onClickFunc);
      textNode = nodeAPI->createNode(ARKUI\_NODE\_TEXT);
      nodeAPI->addChild(columnTest, textNode);
      AttributeUtil textAttr(textNode, nodeAPI);
      textAttr.text("hello word");
      OH\_ArkUI\_NodeContent\_AddNode(contentHandle, columnTest);
      return nullptr;
    }
    EXTERN\_C\_START
    static napi\_value Init(napi\_env env, napi\_value exports) {
      napi\_property\_descriptor desc\[\] = {
        {"createNativeNode", nullptr, NAPI\_Global\_createNativeNode, nullptr, nullptr, nullptr, napi\_default, nullptr}};
    napi\_define\_properties(env, exports, sizeof(desc) / sizeof(desc\[0\]), desc);
    return exports;
    }
    EXTERN\_C\_END
    
    static napi\_module demoModule = {
      .nm\_version = 1,
      .nm\_flags = 0,
      .nm\_filename = nullptr,
      .nm\_register\_func = Init,
      .nm\_modname = "entry",
      .nm\_priv = ((void \*)0),
      .reserved = {0},
    };
    
    extern "C" \_\_attribute\_\_((constructor)) void RegisterEntryModule(void) { napi\_module\_register(&demoModule); }
    
4.  运行程序，点击change text visibility后打印text是否在渲染树上。
    
    ![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/cb/v3/sFVbU0d4Sb2eoqsCFg1lpA/zh-cn_image_0000002625107791.png?HW-CC-KV=V1&HW-CC-Date=20260612T073317Z&HW-CC-Expire=86400&HW-CC-Sign=5A893E34F20D5843BF01C2FFB7E178465739301221397B0A7092DCD4F74E4F45)