构建自定义组件
=======

ArkUI开发框架在NDK接口提供了自定义UI组件的能力，这些能力包括自定义测算，自定义布局和自定义绘制。开发者通过注册相关自定义回调事件接入ArkUI开发框架的布局渲染流程，这些事件需要使用[registerNodeCustomEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#registernodecustomevent)来进行声明，并通过[addNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#addnodecustomeventreceiver)函数添加组件自定义事件的监听器，在该监听器的回调函数中处理相关自定义测算，自定义布局和自定义绘制逻辑。

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/d9/v3/ZQaS8asYQq60H9l8p22CjA/note_3.0-zh-cn.png?HW-CC-KV=V1&HW-CC-Date=20260709T132316Z&HW-CC-Expire=86400&HW-CC-Sign=80B63F3A2F2CD45575C17DA7495F8D1DAFCEA8C9E23BDB64C7BF5835174C87E3)

*   自定义组件事件注册需要[addNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#addnodecustomeventreceiver)声明监听器注册和[registerNodeCustomEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#registernodecustomevent)声明需要的自定义事件类型，监听器只能监听已声明的事件。
    
*   需要关注事件的反注册逻辑，如在组件销毁前调用[removeNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#removenodecustomeventreceiver)移除事件监听器，[unregisterNodeCustomEvent](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#unregisternodecustomevent)通知ArkUI框架已监听的自定义组件事件不再需要监听。
    
*   [addNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#addnodecustomeventreceiver)可以添加多个函数指针，每个函数指针都会在对应事件触发时触发，对应的[removeNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#removenodecustomeventreceiver)需要传递对应的函数指针用于移除监听。
    
*   [registerNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#registernodecustomeventreceiver)是全局监听函数，不同于[addNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#addnodecustomeventreceiver)，[registerNodeCustomEventReceiver](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#registernodecustomeventreceiver)能够监听所有Native组件的自定义事件触发，但只能传递一个函数指针，多次调用使用最后一次的函数指针进行回调，释放时使用unregisterNodeCustomEventReceiver进行反注册。
    
*   自定义组件相关接口（[measureNode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#measurenode)、[layoutNode](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#layoutnode)、[setMeasuredSize](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#setmeasuredsize)、[setLayoutPosition](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-arkui-nativemodule-arkui-nativenodeapi-1#setlayoutposition)）仅允许在对应的自定义事件（**ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_MEASURE**、**ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_LAYOUT**，请参考[ArkUI\_NodeCustomEventType](https://developer.huawei.com/consumer/cn/doc/harmonyos-references/capi-native-node-h#arkui_nodecustomeventtype)）回调中使用。
    

#### 自定义布局容器

以下示例创建了一个自定义容器，该容器将子组件最大值加上额外边距作为自身大小，同时对子组件进行居中排布。

**图1** 自定义容器组件

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/06/v3/5JbYA7k3T-u23REWupLtoA/zh-cn_image_0000002634008700.png?HW-CC-KV=V1&HW-CC-Date=20260709T132316Z&HW-CC-Expire=86400&HW-CC-Sign=62BDE2ABB7221B250A82A736CF492CA88B8CE8B12339BD89E5707BB3E2B1F496)

1.  按照[接入ArkTS页面](https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/ndk-access-the-arkts-page)创建前置工程。
    
2.  创建自定义容器组件封装对象。
    
    // ArkUICustomContainerNode.h
    // 自定义容器组件示例
    
    #ifndef MYAPPLICATION\_ARKUICUSTOMCONTAINERNODE\_H
    #define MYAPPLICATION\_ARKUICUSTOMCONTAINERNODE\_H
    
    #include "ArkUINode.h"
    
    namespace NativeModule {
    
        class ArkUICustomContainerNode : public ArkUINode {
        public:
            // 使用自定义组件类型ARKUI\_NODE\_CUSTOM创建组件。
            ArkUICustomContainerNode()
                : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI\_NODE\_CUSTOM))
            {
                // 注册自定义事件监听器。
                nativeModule\_->addNodeCustomEventReceiver(handle\_, OnStaticCustomEvent);
                // 声明自定义事件并传递自身作为自定义数据。
                nativeModule\_->registerNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_MEASURE, 0, this);
                nativeModule\_->registerNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_LAYOUT, 0, this);
            }
    
            ~ArkUICustomContainerNode() override
            {
                // 反注册自定义事件监听器。
                nativeModule\_->removeNodeCustomEventReceiver(handle\_, OnStaticCustomEvent);
                // 取消声明自定义事件。
                nativeModule\_->unregisterNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_MEASURE);
                nativeModule\_->unregisterNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_LAYOUT);
            }
    
            void SetPadding(int32\_t padding)
            {
                padding\_ = padding;
                // 自定义属性事件更新需要主动调用标记脏区接口。
                nativeModule\_->markDirty(handle\_, NODE\_NEED\_MEASURE);
            }
    
        private:
            static void OnStaticCustomEvent(ArkUI\_NodeCustomEvent \*event)
            {
                // 获取组件实例对象，调用相关实例方法。
                auto customNode = reinterpret\_cast<ArkUICustomContainerNode \*>(OH\_ArkUI\_NodeCustomEvent\_GetUserData(event));
                auto type = OH\_ArkUI\_NodeCustomEvent\_GetEventType(event);
                switch (type) {
                    case ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_MEASURE:
                        customNode->OnMeasure(event);
                        break;
                    case ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_LAYOUT:
                        customNode->OnLayout(event);
                        break;
                    default:
                        break;
                }
            }
    
            // 自定义测算逻辑。
            void OnMeasure(ArkUI\_NodeCustomEvent \*event)
            {
                auto layoutConstrain = OH\_ArkUI\_NodeCustomEvent\_GetLayoutConstraintInMeasure(event);
                // 创建子节点布局限制，复用父组件布局中的百分比参考值。
                auto childLayoutConstrain = OH\_ArkUI\_LayoutConstraint\_Copy(layoutConstrain);
                int32\_t maxConstrain = 1000;
                OH\_ArkUI\_LayoutConstraint\_SetMaxHeight(childLayoutConstrain, maxConstrain);
                OH\_ArkUI\_LayoutConstraint\_SetMaxWidth(childLayoutConstrain, maxConstrain);
                OH\_ArkUI\_LayoutConstraint\_SetMinHeight(childLayoutConstrain, 0);
                OH\_ArkUI\_LayoutConstraint\_SetMinWidth(childLayoutConstrain, 0);
    
                // 测算子节点获取子节点最大值。
                auto totalSize = nativeModule\_->getTotalChildCount(handle\_);
                int32\_t maxWidth = 0;
                int32\_t maxHeight = 0;
                for (uint32\_t i = 0; i < totalSize; i++) {
                    auto child = nativeModule\_->getChildAt(handle\_, i);
                    // 调用测算接口测算Native组件。
                    nativeModule\_->measureNode(child, childLayoutConstrain);
                    auto size = nativeModule\_->getMeasuredSize(child);
                    if (size.width > maxWidth) {
                        maxWidth = size.width;
                    }
                    if (size.height > maxHeight) {
                        maxHeight = size.height;
                    }
                }
                // 自定义测算为所有子节点大小加固定边距。该自定义节点最终的尺寸以此处设置的值为准。
                const int paddingMultiplier = 2;
                nativeModule\_->setMeasuredSize(handle\_, maxWidth + paddingMultiplier \* padding\_,
                                               maxHeight + paddingMultiplier \* padding\_);
            }
    
            void OnLayout(ArkUI\_NodeCustomEvent \*event)
            {
                // 获取父组件期望位置并设置。
                auto position = OH\_ArkUI\_NodeCustomEvent\_GetPositionInLayout(event);
                nativeModule\_->setLayoutPosition(handle\_, position.x, position.y);
    
                // 设置子组件居中对齐。
                auto totalSize = nativeModule\_->getTotalChildCount(handle\_);
                auto selfSize = nativeModule\_->getMeasuredSize(handle\_);
                for (uint32\_t i = 0; i < totalSize; i++) {
                    auto child = nativeModule\_->getChildAt(handle\_, i);
                    // 获取子组件大小。
                    auto childSize = nativeModule\_->getMeasuredSize(child);
                    // 布局子组件位置。
                    int32\_t horizontalMargin = (selfSize.width - childSize.width) / 2;
                    int32\_t verticalMargin = (selfSize.height - childSize.height) / 2;
                    nativeModule\_->layoutNode(child, horizontalMargin, verticalMargin);
                }
            }
    
            int32\_t padding\_ = 100;
        };
    
    } // namespace NativeModule
    
    #endif // MYAPPLICATION\_ARKUICUSTOMCONTAINERNODE\_H
    
3.  使用自定义容器创建带文本的示例界面。
    
    #include "NativeEntry.h"
    
    #include "ArkUICustomContainerNode.h"
    #include "ArkUITextNode.h"
    #include "UITimer.h"
    
    #include <arkui/native\_node\_napi.h>
    #include <arkui/native\_type.h>
    #include <js\_native\_api.h>
    
    namespace NativeModule {
        namespace {
            napi\_env g\_env;
        } // namespace
    
        napi\_value CreateNativeRoot(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);
            NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    
            // 创建自定义容器和文本组件。
            auto node = std::make\_shared<ArkUICustomContainerNode>();
            node->SetBackgroundColor(0xFFE0FFFF);
            auto textNode = std::make\_shared<ArkUITextNode>();
            textNode->SetTextContent("CustomContainer Example");
            const int32\_t fontSize = 16;
            textNode->SetFontSize(fontSize);
            textNode->SetBackgroundColor(0xFFfffacd);
            textNode->SetTextAlign(ARKUI\_TEXT\_ALIGNMENT\_CENTER);
            node->AddChild(textNode);
            auto onClick = \[\](ArkUI\_NodeEvent \*event) {
                auto textNode = (ArkUITextNode \*)OH\_ArkUI\_NodeEvent\_GetUserData(event);
                textNode->SetFontColor(0xFF00FF7F);
            };
            textNode->RegisterOnClick(onClick, textNode.get());
    
            // 保持Native侧对象到管理类中，维护生命周期。
            NativeEntry::GetInstance()->SetRootNode(node);
            g\_env = env;
            return nullptr;
        }
    
        napi\_value DestroyNativeRoot(napi\_env env, napi\_callback\_info info)
        {
            // 从管理类中释放Native侧对象。
            NativeEntry::GetInstance()->DisposeRootNode();
            return nullptr;
        }
    
    } // namespace NativeModule
    
4.  修改CMakeLists.txt，添加链接库。
    
      # CMakeLists.txt
    
      # the minimum version of CMake.
      cmake\_minimum\_required(VERSION 3.4.1)
      project(testndk)
    
      set(NATIVERENDER\_ROOT\_PATH ${CMAKE\_CURRENT\_SOURCE\_DIR})
    
      include\_directories(${NATIVERENDER\_ROOT\_PATH}
                           ${NATIVERENDER\_ROOT\_PATH}/include)
    
      add\_library(entry SHARED NativeEntry.cpp napi\_init.cpp)
      # target\_link\_libraries(entry PUBLIC libace\_napi.z.so, libace\_ndk.z.so, libhilog\_ndk.z.so)
    
      find\_library(
           # Sets the name of the path variable.
           hilog-lib
           # Specifies the name of the NDK library that
           # you want CMake to locate.
           hilog\_ndk.z
       )
    
      find\_library(
           # Sets the name of the path variable.
           libace-lib
           # Specifies the name of the NDK library that
           # you want CMake to locate.
           ace\_ndk.z
       )
    
      find\_library(
           # Sets the name of the path variable.
           libnapi-lib
           # Specifies the name of the NDK library that
           # you want CMake to locate.
           ace\_napi.z
       )
    
       find\_library(
            # Sets the name of the path variable.
            libuv-lib
            uv
        )
    
      target\_link\_libraries(entry PUBLIC
           ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} )
    

#### 自定义绘制组件

以下示例创建了一个自定义绘制组件，该绘制组件能够绘制自定义矩形，并使用上述自定义容器进行布局排布。

**图2** 自定义绘制组件

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/4e/v3/G5ZWNtL_QUqAQWmmilBqyw/zh-cn_image_0000002664207847.png?HW-CC-KV=V1&HW-CC-Date=20260709T132316Z&HW-CC-Expire=86400&HW-CC-Sign=E670C60A4EE96EF9721933E2221DFCF745E52D1A12259C6C656B3ED3F2812DA5)

1.  按照[自定义布局容器](#自定义布局容器)章节准备前置工程。
    
2.  创建自定义绘制组件封装对象。
    
    // ArkUICustomNode.h
    // 自定义绘制组件示例
    
    #ifndef MYAPPLICATION\_ARKUICUSTOMNODE\_H
    #define MYAPPLICATION\_ARKUICUSTOMNODE\_H
    
    #include <native\_drawing/drawing\_brush.h>
    #include <native\_drawing/drawing\_canvas.h>
    #include <native\_drawing/drawing\_path.h>
    
    #include "ArkUINode.h"
    
    namespace NativeModule {
    
        class ArkUICustomNode : public ArkUINode {
        public:
            // 使用自定义组件类型ARKUI\_NODE\_CUSTOM创建组件。
            ArkUICustomNode()
                : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI\_NODE\_CUSTOM))
            {
                // 注册自定义事件监听器。
                nativeModule\_->addNodeCustomEventReceiver(handle\_, OnStaticCustomEvent);
                // 声明自定义事件并传递自身作为自定义数据。
                nativeModule\_->registerNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_DRAW, 0, this);
            }
    
            ~ArkUICustomNode() override
            {
                // 反注册自定义事件监听器。
                nativeModule\_->removeNodeCustomEventReceiver(handle\_, OnStaticCustomEvent);
                // 取消声明自定义事件。
                nativeModule\_->unregisterNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_DRAW);
            }
    
            void SetRectColor(uint32\_t color)
            {
                color\_ = color;
                // 自定义绘制属性变更需要主动通知框架。
                nativeModule\_->markDirty(handle\_, NODE\_NEED\_RENDER);
            }
    
        private:
            static void OnStaticCustomEvent(ArkUI\_NodeCustomEvent \*event)
            {
                // 获取组件实例对象，调用相关实例方法。
                auto customNode = reinterpret\_cast<ArkUICustomNode \*>(OH\_ArkUI\_NodeCustomEvent\_GetUserData(event));
                auto type = OH\_ArkUI\_NodeCustomEvent\_GetEventType(event);
                switch (type) {
                    case ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_DRAW:
                        customNode->OnDraw(event);
                        break;
                    default:
                        break;
                }
            }
    
            // 自定义绘制逻辑。
            void OnDraw(ArkUI\_NodeCustomEvent \*event)
            {
                auto drawContext = OH\_ArkUI\_NodeCustomEvent\_GetDrawContextInDraw(event);
                // 获取图形绘制对象。
                auto drawCanvas = reinterpret\_cast<OH\_Drawing\_Canvas \*>(OH\_ArkUI\_DrawContext\_GetCanvas(drawContext));
                // 获取组件大小。
                auto size = OH\_ArkUI\_DrawContext\_GetSize(drawContext);
                // 绘制自定义内容。
                auto path = OH\_Drawing\_PathCreate();
                const float kQuarter = 0.25f;
                const float kThreeQuarters = 0.75f;
                OH\_Drawing\_PathMoveTo(path, size.width \* kQuarter, size.height \* kQuarter);
                OH\_Drawing\_PathLineTo(path, size.width \* kThreeQuarters, size.height \* kQuarter);
                OH\_Drawing\_PathLineTo(path, size.width \* kThreeQuarters, size.height \* kThreeQuarters);
                OH\_Drawing\_PathLineTo(path, size.width \* kQuarter, size.height \* kThreeQuarters);
                OH\_Drawing\_PathLineTo(path, size.width \* kQuarter, size.height \* kQuarter);
                OH\_Drawing\_PathClose(path);
                auto brush = OH\_Drawing\_BrushCreate();
                OH\_Drawing\_BrushSetColor(brush, color\_);
                OH\_Drawing\_CanvasAttachBrush(drawCanvas, brush);
                OH\_Drawing\_CanvasDrawPath(drawCanvas, path);
                // 释放资源
                OH\_Drawing\_BrushDestroy(brush);
                OH\_Drawing\_PathDestroy(path);
            }
    
            uint32\_t color\_ = 0xFFFFE4B5;
        };
    
    } // namespace NativeModule
    
    #endif // MYAPPLICATION\_ARKUICUSTOMNODE\_H
    
3.  使用自定义绘制组件和自定义容器创建示例界面。
    
    #include "NativeEntry.h"
    
    #include "ArkUICustomContainerNode.h"
    #include "ArkUICustomNode.h"
    
    #include <arkui/native\_node\_napi.h>
    #include <arkui/native\_type.h>
    #include <js\_native\_api.h>
    #include "UITimer.h"
    
    namespace NativeModule {
        namespace {
            napi\_env g\_env;
        } // namespace
    
        napi\_value CreateNativeRoot(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);
    
            // 获取NodeContent
            ArkUI\_NodeContentHandle contentHandle;
            OH\_ArkUI\_GetNodeContentFromNapiValue(env, args\[0\], &contentHandle);
            NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    
            // 创建自定义容器和自定义绘制组件。
            auto node = std::make\_shared<ArkUICustomContainerNode>();
            node->SetBackgroundColor(0xFFE0FFFF);
            auto customNode = std::make\_shared<ArkUICustomNode>();
            customNode->SetBackgroundColor(0xFFD3D3D3);
            const int width = 150;
            const int height = 150;
            customNode->SetWidth(width);
            customNode->SetHeight(height);
            node->AddChild(customNode);
            auto onClick = \[\](ArkUI\_NodeEvent \*event) {
                auto customNode = (ArkUICustomNode \*)OH\_ArkUI\_NodeEvent\_GetUserData(event);
                customNode->SetRectColor(0xFF00FF7F);
            };
            customNode->RegisterOnClick(onClick, customNode.get());
    
            // 保持Native侧对象到管理类中，维护生命周期。
            NativeEntry::GetInstance()->SetRootNode(node);
            g\_env = env;
            return nullptr;
        }
    
        napi\_value DestroyNativeRoot(napi\_env env, napi\_callback\_info info)
        {
            // 从管理类中释放Native侧对象。
            NativeEntry::GetInstance()->DisposeRootNode();
            return nullptr;
        }
    
    } // namespace NativeModule
    
4.  修改CMakeLists.txt，添加链接库。
    
      # CMakeLists.txt
    
      # the minimum version of CMake.
      cmake\_minimum\_required(VERSION 3.4.1)
      project(testndk)
    
      set(NATIVERENDER\_ROOT\_PATH ${CMAKE\_CURRENT\_SOURCE\_DIR})
    
      include\_directories(${NATIVERENDER\_ROOT\_PATH}
                           ${NATIVERENDER\_ROOT\_PATH}/include)
    
      add\_library(entry SHARED NativeEntry.cpp napi\_init.cpp)
      # target\_link\_libraries(entry PUBLIC libace\_napi.z.so, libace\_ndk.z.so, libhilog\_ndk.z.so)
    
      find\_library(
           # Sets the name of the path variable.
           hilog-lib
           # Specifies the name of the NDK library that
           # you want CMake to locate.
           hilog\_ndk.z
       )
    
      find\_library(
           # Sets the name of the path variable.
           libace-lib
           # Specifies the name of the NDK library that
           # you want CMake to locate.
           ace\_ndk.z
       )
    
      find\_library(
           # Sets the name of the path variable.
           libnapi-lib
           # Specifies the name of the NDK library that
           # you want CMake to locate.
           ace\_napi.z
       )
    
       find\_library(
            # Sets the name of the path variable.
            libuv-lib
            uv
        )
    
      target\_link\_libraries(entry PUBLIC
           ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative\_drawing.so)
    

#### 不规则网格布局示例

以下示例创建了一个不规则网格布局容器，支持不同大小的网格单元，实现类似瀑布流的布局效果。完整示例请参考[CustomDrawIrregularSample](https://gitcode.com/HarmonyOS_Samples/guide-snippets/tree/master/ArkUISample/NativeType/CustomDrawIrregularSample)。

**图3** 不规则网格布局效果

![](https://contentcenter-vali-drcn.dbankcdn.cn/pvt_2/DeveloperAlliance_scene_100_1/82/v3/sfldhA86TiGCPbpQYCWW5g/zh-cn_image_0000002664327907.jpg?HW-CC-KV=V1&HW-CC-Date=20260709T132316Z&HW-CC-Expire=86400&HW-CC-Sign=77BF7A75288576F50502A7E14749E1295B153CBD1A03D6F8777F87FFAC13479C)

1.  按照[自定义布局容器](#自定义布局容器)章节准备前置工程。
    
2.  创建不规则网格布局容器组件封装对象。
    
     // ArkUIIrregularGridNode.h
     // 不规则网格布局容器示例
    
     #ifndef MYAPPLICATION\_ARKUIIRREGULARGRIDNODE\_H
     #define MYAPPLICATION\_ARKUIIRREGULARGRIDNODE\_H
    
     #include "ArkUINode.h"
     #include <vector>
     #include <map>
    
     namespace NativeModule {
    
     // 网格单元配置
     struct GridItemConfig {
         int32\_t rowSpan = 1;    // 占据的行数
         int32\_t columnSpan = 1; // 占据的列数
     };
    
     class ArkUIIrregularGridNode : public ArkUINode {
     public:
         // 使用自定义组件类型ARKUI\_NODE\_CUSTOM创建组件
         ArkUIIrregularGridNode()
             : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI\_NODE\_CUSTOM))
         {
             // 注册自定义事件监听器
             nativeModule\_->addNodeCustomEventReceiver(handle\_, OnStaticCustomEvent);
             // 声明自定义事件并传递自身作为自定义数据
             nativeModule\_->registerNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_MEASURE, 0, this);
             nativeModule\_->registerNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_LAYOUT, 0, this);
         }
    
         ~ArkUIIrregularGridNode() override
         {
             // 反注册自定义事件监听器
             nativeModule\_->removeNodeCustomEventReceiver(handle\_, OnStaticCustomEvent);
             // 取消声明自定义事件。
             nativeModule\_->unregisterNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_MEASURE);
             nativeModule\_->unregisterNodeCustomEvent(handle\_, ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_LAYOUT);
         }
    
         // 设置列数
         void SetColumnCount(int32\_t count)
         {
             columnCount\_ = count;
             nativeModule\_->markDirty(handle\_, NODE\_NEED\_MEASURE);
         }
    
         // 设置网格间距
         void SetGap(int32\_t gap)
         {
             gap\_ = gap;
             nativeModule\_->markDirty(handle\_, NODE\_NEED\_MEASURE);
         }
    
         // 设置子组件的网格配置
         void SetItemConfig(ArkUI\_NodeHandle child, int32\_t rowSpan, int32\_t columnSpan)
         {
             GridItemConfig config;
             config.rowSpan = rowSpan;
             config.columnSpan = columnSpan;
             itemConfigs\_\[child\] = config;
             nativeModule\_->markDirty(handle\_, NODE\_NEED\_MEASURE);
         }
    
     private:
         static void OnStaticCustomEvent(ArkUI\_NodeCustomEvent \*event)
         {
             // 获取组件实例对象，调用相关实例方法。
             auto customNode = reinterpret\_cast<ArkUIIrregularGridNode \*>(OH\_ArkUI\_NodeCustomEvent\_GetUserData(event));
             auto type = OH\_ArkUI\_NodeCustomEvent\_GetEventType(event);
             switch (type) {
                 case ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_MEASURE:
                     customNode->OnMeasure(event);
                     break;
                 case ARKUI\_NODE\_CUSTOM\_EVENT\_ON\_LAYOUT:
                     customNode->OnLayout(event);
                     break;
                 default:
                     break;
             }
         }
    
         // 测算单个子组件并更新列高信息
         void MeasureChild(ArkUI\_NodeHandle child, int32\_t cellWidth,
             ArkUI\_LayoutConstraint \*childConstraint, std::vector<int32\_t> &columnHeights)
         {
             GridItemConfig config = {1, 1};
             auto it = itemConfigs\_.find(child);
             if (it != itemConfigs\_.end()) {
                 config = it->second;
             }
             if (config.columnSpan > columnCount\_) {
                 config.columnSpan = columnCount\_;
             }
    
             int32\_t startColumn = FindLowestColumn(columnHeights, config.columnSpan);
             int32\_t startY = 0;
             for (int32\_t col = startColumn; col < startColumn + config.columnSpan && col < columnCount\_; col++) {
                 if (columnHeights\[col\] > startY) {
                     startY = columnHeights\[col\];
                 }
             }
    
             int32\_t itemWidth = cellWidth \* config.columnSpan + gap\_ \* (config.columnSpan - 1);
             OH\_ArkUI\_LayoutConstraint\_SetMaxWidth(childConstraint, itemWidth);
             OH\_ArkUI\_LayoutConstraint\_SetMinWidth(childConstraint, itemWidth);
             nativeModule\_->measureNode(child, childConstraint);
             auto size = nativeModule\_->getMeasuredSize(child);
    
             LayoutItemInfo info;
             info.x = startColumn \* (cellWidth + gap\_);
             info.y = startY;
             info.width = size.width;
             info.height = size.height;
             layoutInfo\_.push\_back(info);
    
             int32\_t newHeight = startY + size.height + gap\_;
             for (int32\_t col = startColumn; col < startColumn + config.columnSpan && col < columnCount\_; col++) {
                 columnHeights\[col\] = newHeight;
             }
         }
    
         // 自定义测算逻辑：不规则网格布局
         void OnMeasure(ArkUI\_NodeCustomEvent \*event)
         {
             auto layoutConstrain = OH\_ArkUI\_NodeCustomEvent\_GetLayoutConstraintInMeasure(event);
             int32\_t maxWidth = OH\_ArkUI\_LayoutConstraint\_GetMaxWidth(layoutConstrain);
    
             int32\_t totalGap = gap\_ \* (columnCount\_ - 1);
             int32\_t cellWidth = (maxWidth - totalGap) / columnCount\_;
    
             auto childConstraint = OH\_ArkUI\_LayoutConstraint\_Copy(layoutConstrain);
             std::vector<int32\_t> columnHeights(columnCount\_, 0);
             layoutInfo\_.clear();
    
             auto totalSize = nativeModule\_->getTotalChildCount(handle\_);
             for (uint32\_t i = 0; i < totalSize; i++) {
                 auto child = nativeModule\_->getChildAt(handle\_, i);
                 MeasureChild(child, cellWidth, childConstraint, columnHeights);
             }
    
             int32\_t maxHeight = 0;
             for (int32\_t height : columnHeights) {
                 if (height > maxHeight) {
                     maxHeight = height;
                 }
             }
             if (maxHeight > gap\_) {
                 maxHeight -= gap\_;
             }
    
             nativeModule\_->setMeasuredSize(handle\_, maxWidth, maxHeight);
             OH\_ArkUI\_LayoutConstraint\_Dispose(childConstraint);
         }
    
         void OnLayout(ArkUI\_NodeCustomEvent \*event)
         {
             // 获取父组件期望位置并设置
             auto position = OH\_ArkUI\_NodeCustomEvent\_GetPositionInLayout(event);
             nativeModule\_->setLayoutPosition(handle\_, position.x, position.y);
    
             // 布局子组件
             auto totalSize = nativeModule\_->getTotalChildCount(handle\_);
             for (uint32\_t i = 0; i < totalSize && i < layoutInfo\_.size(); i++) {
                 auto child = nativeModule\_->getChildAt(handle\_, i);
                 nativeModule\_->layoutNode(child, layoutInfo\_\[i\].x, layoutInfo\_\[i\].y);
             }
         }
    
         // 找到最矮的列，确保可以放下指定列跨度的项
         int32\_t FindLowestColumn(const std::vector<int32\_t>& columnHeights, int32\_t columnSpan)
         {
             int32\_t lowestColumn = 0;
             int32\_t lowestHeight = INT32\_MAX;
    
             // 遍历所有可能的起始列
             for (int32\_t col = 0; col <= columnCount\_ - columnSpan; col++) {
                 // 找到这个范围内最高的列
                 int32\_t maxHeightInRange = 0;
                 for (int32\_t i = col; i < col + columnSpan; i++) {
                     if (columnHeights\[i\] > maxHeightInRange) {
                         maxHeightInRange = columnHeights\[i\];
                     }
                 }
    
                 // 如果这个范围的最高点比当前最低点还低，更新最低列
                 if (maxHeightInRange < lowestHeight) {
                     lowestHeight = maxHeightInRange;
                     lowestColumn = col;
                 }
             }
    
             return lowestColumn;
         }
    
         struct LayoutItemInfo {
             int32\_t x;
             int32\_t y;
             int32\_t width;
             int32\_t height;
         };
    
         int32\_t columnCount\_ = 3;
         int32\_t gap\_ = 10;
         std::map<ArkUI\_NodeHandle, GridItemConfig> itemConfigs\_;
         std::vector<LayoutItemInfo> layoutInfo\_;
     };
    
     } // namespace NativeModule
    
     #endif // MYAPPLICATION\_ARKUIIRREGULARGRIDNODE\_H
    
3.  使用不规则网格布局容器创建示例界面。
    
     #include "NativeEntry.h"
    
     #include "ArkUIIrregularGridNode.h"
     #include "ArkUINode.h"
    
     #include <arkui/native\_node\_napi.h>
     #include <arkui/native\_type.h>
     #include <js\_native\_api.h>
     #include <utility>
     #include <vector>
    
     namespace NativeModule {
     namespace {
     napi\_env g\_env = nullptr;
    
     constexpr uint32\_t GRID\_BACKGROUND\_COLOR = 0xFFF5F5F5;
     constexpr int32\_t GRID\_COLUMN\_COUNT = 4;
     constexpr int32\_t GRID\_GAP = 8;
     constexpr float GRID\_ITEM\_RADIUS = 8.0f;
     constexpr float GRID\_ITEM\_BORDER\_WIDTH = 1.0f;
     constexpr uint32\_t GRID\_ITEM\_BORDER\_COLOR = 0xFFCCCCCC;
     constexpr float GRID\_ITEM\_BASE\_HEIGHT = 60.0f;
     constexpr float GRID\_ITEM\_HEIGHT\_STEP = 40.0f;
    
     using GridItemSize = std::pair<int32\_t, int32\_t>;
    
     const std::vector<GridItemSize>& GetGridItemSizes()
     {
         static const std::vector<GridItemSize> itemSizes = {
             {1, 1}, // 小方块
             {2, 1}, // 竖长条
             {1, 3}, // 横长条
             {2, 2}, // 大方块
             {1, 1}, // 小方块
             {1, 2}, // 横条
             {3, 1}, // 很长的竖条
         };
         return itemSizes;
     }
    
     const std::vector<uint32\_t>& GetGridItemColors()
     {
         static const std::vector<uint32\_t> colors = {
             0xFF64B5F6, // 蓝色
             0xFFE57373, // 红色
             0xFF81C784, // 绿色
             0xFFFFB74D, // 橙色
             0xFF9575CD, // 紫色
             0xFF4DB6AC, // 青色
             0xFFFFD54F, // 黄色
             0xFFF06292, // 粉色
             0xFF7986CB, // 靛蓝
             0xFFA1887F, // 棕色
         };
         return colors;
     }
    
     void SetNodeColorAttribute(ArkUI\_NativeNodeAPI\_1\* nodeAPI, ArkUI\_NodeHandle node, uint32\_t color)
     {
         ArkUI\_NumberValue bgColor\[\] = {{.u32 = color}};
         ArkUI\_AttributeItem bgColorItem = {bgColor, 1};
         nodeAPI->setAttribute(node, NODE\_BACKGROUND\_COLOR, &bgColorItem);
     }
    
     void SetNodeBorderRadiusAttribute(ArkUI\_NativeNodeAPI\_1\* nodeAPI, ArkUI\_NodeHandle node, float radius)
     {
         ArkUI\_NumberValue radiusValue\[\] = {{.f32 = radius}};
         ArkUI\_AttributeItem radiusItem = {radiusValue, 1};
         nodeAPI->setAttribute(node, NODE\_BORDER\_RADIUS, &radiusItem);
     }
    
     void SetNodeBorderStyle(ArkUI\_NativeNodeAPI\_1\* nodeAPI, ArkUI\_NodeHandle node)
     {
         ArkUI\_NumberValue borderWidth\[\] = {{.f32 = GRID\_ITEM\_BORDER\_WIDTH}};
         ArkUI\_AttributeItem borderWidthItem = {borderWidth, 1};
         nodeAPI->setAttribute(node, NODE\_BORDER\_WIDTH, &borderWidthItem);
    
         ArkUI\_NumberValue borderColor\[\] = {{.u32 = GRID\_ITEM\_BORDER\_COLOR}};
         ArkUI\_AttributeItem borderColorItem = {borderColor, 1};
         nodeAPI->setAttribute(node, NODE\_BORDER\_COLOR, &borderColorItem);
     }
    
     void SetNodeHeightByRowSpan(ArkUI\_NativeNodeAPI\_1\* nodeAPI, ArkUI\_NodeHandle node, int32\_t rowSpan)
     {
         float minHeight = GRID\_ITEM\_BASE\_HEIGHT + (rowSpan - 1) \* GRID\_ITEM\_HEIGHT\_STEP;
         ArkUI\_NumberValue minHeightValue\[\] = {{.f32 = minHeight}};
         ArkUI\_AttributeItem minHeightItem = {minHeightValue, 1};
         nodeAPI->setAttribute(node, NODE\_HEIGHT, &minHeightItem);
     }
    
     void AddGridItems(
         ArkUI\_NativeNodeAPI\_1\* nodeAPI,
         const std::shared\_ptr<ArkUIIrregularGridNode>& gridNode,
         const std::vector<GridItemSize>& itemSizes,
         const std::vector<uint32\_t>& colors)
     {
         for (size\_t i = 0; i < itemSizes.size(); ++i) {
             auto itemNode = nodeAPI->createNode(ARKUI\_NODE\_STACK);
             SetNodeColorAttribute(nodeAPI, itemNode, colors\[i % colors.size()\]);
             SetNodeBorderRadiusAttribute(nodeAPI, itemNode, GRID\_ITEM\_RADIUS);
             SetNodeBorderStyle(nodeAPI, itemNode);
             SetNodeHeightByRowSpan(nodeAPI, itemNode, itemSizes\[i\].first);
             gridNode->SetItemConfig(itemNode, itemSizes\[i\].first, itemSizes\[i\].second);
             nodeAPI->addChild(gridNode->GetHandle(), itemNode);
         }
     }
     } // namespace
    
     napi\_value CreateNativeRoot(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);
         NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    
         auto gridNode = std::make\_shared<ArkUIIrregularGridNode>();
         gridNode->SetBackgroundColor(GRID\_BACKGROUND\_COLOR);
         gridNode->SetColumnCount(GRID\_COLUMN\_COUNT);
         gridNode->SetGap(GRID\_GAP);
    
         auto\* nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
         AddGridItems(nodeAPI, gridNode, GetGridItemSizes(), GetGridItemColors());
    
         // 保持Native侧对象到管理类中，维护生命周期。
         NativeEntry::GetInstance()->SetRootNode(gridNode);
         g\_env = env;
         return nullptr;
     }
    
     napi\_value DestroyNativeRoot(napi\_env env, napi\_callback\_info info)
     {
         // 从管理类中释放Native侧对象。
         NativeEntry::GetInstance()->DisposeRootNode();
         return nullptr;
     }
    
     } // namespace NativeModule