# 查询和操作NDK节点

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获取目标节点的指针。

```cpp
const unsigned int VALUE_1 = 480;
const unsigned int VALUE_2 = 300;
const unsigned int VALUE_3 = 50;
    std::shared_ptr<ArkUIBaseNode> InquireUniqueId::GetNodeUniqueId()
    {
        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;
        };
        static IdList idl;
        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组件。

   ```TypeScript
   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接口拿到节点。

   ```c
   // 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();
      
      // 创建根节点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);
      
      // 创建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, text0);
      
      // 注册事件
      auto onClick = [](ArkUI_NodeEvent *event) {
          auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();

          if (OH_ArkUI_NodeEvent_GetTargetId(event) == 0) {  // GetAttachedNodeHandleById
              auto text0 = (ArkUI_NodeHandle)OH_ArkUI_NodeEvent_GetUserData(event);
              ArkUI_NodeHandle node = nullptr;
              auto res = OH_ArkUI_NodeUtils_GetAttachedNodeHandleById("Text0_CAPI", &node);
              if (res == ARKUI_ERROR_CODE_NO_ERROR && node == text0) {
                  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节点。

   ```cpp
   // 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节点移动到新的父节点下，从而按需改变节点树结构。
> 说明
>
> 当前仅支持以下类型的[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组件。

   ```TypeScript
   // 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节点。

   ```c
   // 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(stack_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) {
        auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
        
        if (OH_ArkUI_NodeEvent_GetTargetId(event) == 0) {  // MoveTo
            ArkUI_NodeHandle eventNode = OH_ArkUI_NodeEvent_GetNodeHandle(event);
            A* a = (A*)OH_ArkUI_NodeEvent_GetUserData(event);
            if (a != nullptr) {
                OH_ArkUI_NodeUtils_MoveTo(a->node, a->targetParent, 2);
                nodeAPI->unregisterNodeEvent(eventNode, NODE_ON_CLICK);
                delete a;
            }
        }
    };
    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节点。

   ```cpp
   // 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/84/v3/hu9c4hOyQHC8z-9aVfpFOQ/zh-cn_image_0000002809009987.gif?HW-CC-KV=V1&HW-CC-Date=20261102T125433Z&HW-CC-Expire=31536000000&HW-CC-Sign=15EDEFCAB4D84E1B7383840301B832C7C6AE8CBB7676980CFBC9C625B2116E02)

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

从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组件。

   ```TypeScript
   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用于设置组件属性。

   ```c
   #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_IMAGE_SYNC_LOAD_value[] = {{.i32 = 1}};
           ArkUI_AttributeItem NODE_IMAGE_SYNC_LOAD_item = {NODE_IMAGE_SYNC_LOAD_value, 1};
           return api_->setAttribute(node_, NODE_IMAGE_SYNC_LOAD, &NODE_IMAGE_SYNC_LOAD_item);
       }
   };
   #endif // MYAPPLICATION_ATTRIBUTE_UTIL_H
   ```

3. 在napi_init.cpp中，挂载Native节点。

   ```cpp
   #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/8a/v3/okaaE9uzTO6wPH_jHjBnIQ/zh-cn_image_0000002809049903.png?HW-CC-KV=V1&HW-CC-Date=20261102T125433Z&HW-CC-Expire=31536000000&HW-CC-Sign=858ADA5E40EC97CE762D875CB20227359E4232A9B51BEF5AB59A58ECD75E80AA)

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

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)接口，可以通过不同的节点展开模式获取对应下标的子节点。
> 说明
>
> 节点展开方式请参考[ArkUI_ExpandMode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-type-h#arkui_expandmode)，此处推荐使用ARKUI_LAZY_EXPAND懒展开方式，智能识别对应场景。

1. 通过ArkTS构造LazyForEach及ArkTS的下树节点展开场景。

   ```ts
   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组件，并通过懒展开模式获取对应的子组件信息。

   ```c
   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 == %{public}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组件。

   ```ts
   //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. 在napi_init.cpp中，挂载Native节点。

       #include "napi/native_api.h"
       #include "Attribute_util.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 state 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 world");
         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/b5/v3/q46SHCklSYKKYHo9fzKMsw/zh-cn_image_0000002779850338.png?HW-CC-KV=V1&HW-CC-Date=20261102T125433Z&HW-CC-Expire=31536000000&HW-CC-Sign=8A3F5DCEE44B02DF8FFA05AB6BAE47EB034E5DDA771CDA25DA1333F677DD9411)

