文档管理中心

构建自定义组件

ArkUI开发框架在NDK接口提供了自定义UI组件的能力,这些能力包括自定义测算,自定义布局和自定义绘制。开发者通过注册相关自定义回调事件接入ArkUI开发框架的布局渲染流程,这些事件需要使用registerNodeCustomEvent来进行声明,并通过addNodeCustomEventReceiver函数添加组件自定义事件的监听器,在该监听器的回调函数中处理相关自定义测算,自定义布局和自定义绘制逻辑。

说明

自定义布局容器

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

图1 自定义容器组件

  1. 按照接入ArkTS页面创建前置工程。

  2. 创建自定义容器组件封装对象。

    收起
    自动换行
    深色代码主题
    复制
    1. // ArkUICustomContainerNode.h
    2. // 自定义容器组件示例
    3. #ifndef MYAPPLICATION_ARKUICUSTOMCONTAINERNODE_H
    4. #define MYAPPLICATION_ARKUICUSTOMCONTAINERNODE_H
    5. #include "ArkUINode.h"
    6. namespace NativeModule {
    7. class ArkUICustomContainerNode : public ArkUINode {
    8. public:
    9. // 使用自定义组件类型ARKUI_NODE_CUSTOM创建组件。
    10. ArkUICustomContainerNode()
    11. : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_CUSTOM))
    12. {
    13. // 注册自定义事件监听器。
    14. nativeModule_->addNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
    15. // 声明自定义事件并传递自身作为自定义数据。
    16. nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE, 0, this);
    17. nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_LAYOUT, 0, this);
    18. }
    19. ~ArkUICustomContainerNode() override
    20. {
    21. // 反注册自定义事件监听器。
    22. nativeModule_->removeNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
    23. // 取消声明自定义事件。
    24. nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE);
    25. nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_LAYOUT);
    26. }
    27. void SetPadding(int32_t padding)
    28. {
    29. padding_ = padding;
    30. // 自定义属性事件更新需要主动调用标记脏区接口。
    31. nativeModule_->markDirty(handle_, NODE_NEED_MEASURE);
    32. }
    33. private:
    34. static void OnStaticCustomEvent(ArkUI_NodeCustomEvent *event)
    35. {
    36. // 获取组件实例对象,调用相关实例方法。
    37. auto customNode = reinterpret_cast<ArkUICustomContainerNode *>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
    38. auto type = OH_ArkUI_NodeCustomEvent_GetEventType(event);
    39. switch (type) {
    40. case ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE:
    41. customNode->OnMeasure(event);
    42. break;
    43. case ARKUI_NODE_CUSTOM_EVENT_ON_LAYOUT:
    44. customNode->OnLayout(event);
    45. break;
    46. default:
    47. break;
    48. }
    49. }
    50. // 自定义测算逻辑。
    51. void OnMeasure(ArkUI_NodeCustomEvent *event)
    52. {
    53. auto layoutConstrain = OH_ArkUI_NodeCustomEvent_GetLayoutConstraintInMeasure(event);
    54. // 创建子节点布局限制,复用父组件布局中的百分比参考值。
    55. auto childLayoutConstrain = OH_ArkUI_LayoutConstraint_Copy(layoutConstrain);
    56. int32_t maxConstrain = 1000;
    57. OH_ArkUI_LayoutConstraint_SetMaxHeight(childLayoutConstrain, maxConstrain);
    58. OH_ArkUI_LayoutConstraint_SetMaxWidth(childLayoutConstrain, maxConstrain);
    59. OH_ArkUI_LayoutConstraint_SetMinHeight(childLayoutConstrain, 0);
    60. OH_ArkUI_LayoutConstraint_SetMinWidth(childLayoutConstrain, 0);
    61. // 测算子节点获取子节点最大值。
    62. auto totalSize = nativeModule_->getTotalChildCount(handle_);
    63. int32_t maxWidth = 0;
    64. int32_t maxHeight = 0;
    65. for (uint32_t i = 0; i < totalSize; i++) {
    66. auto child = nativeModule_->getChildAt(handle_, i);
    67. // 调用测算接口测算Native组件。
    68. nativeModule_->measureNode(child, childLayoutConstrain);
    69. auto size = nativeModule_->getMeasuredSize(child);
    70. if (size.width > maxWidth) {
    71. maxWidth = size.width;
    72. }
    73. if (size.height > maxHeight) {
    74. maxHeight = size.height;
    75. }
    76. }
    77. // 自定义测算为所有子节点大小加固定边距。该自定义节点最终的尺寸以此处设置的值为准。
    78. const int paddingMultiplier = 2;
    79. nativeModule_->setMeasuredSize(handle_, maxWidth + paddingMultiplier * padding_,
    80. maxHeight + paddingMultiplier * padding_);
    81. OH_ArkUI_LayoutConstraint_Dispose(childLayoutConstrain);
    82. }
    83. void OnLayout(ArkUI_NodeCustomEvent *event)
    84. {
    85. // 获取父组件期望位置并设置。
    86. auto position = OH_ArkUI_NodeCustomEvent_GetPositionInLayout(event);
    87. nativeModule_->setLayoutPosition(handle_, position.x, position.y);
    88. // 设置子组件居中对齐。
    89. auto totalSize = nativeModule_->getTotalChildCount(handle_);
    90. auto selfSize = nativeModule_->getMeasuredSize(handle_);
    91. for (uint32_t i = 0; i < totalSize; i++) {
    92. auto child = nativeModule_->getChildAt(handle_, i);
    93. // 获取子组件大小。
    94. auto childSize = nativeModule_->getMeasuredSize(child);
    95. // 布局子组件位置。
    96. int32_t horizontalMargin = (selfSize.width - childSize.width) / 2;
    97. int32_t verticalMargin = (selfSize.height - childSize.height) / 2;
    98. nativeModule_->layoutNode(child, horizontalMargin, verticalMargin);
    99. }
    100. }
    101. int32_t padding_ = 100;
    102. };
    103. } // namespace NativeModule
    104. #endif // MYAPPLICATION_ARKUICUSTOMCONTAINERNODE_H
  3. 使用自定义容器创建带文本的示例界面。

    收起
    自动换行
    深色代码主题
    复制
    1. #include "NativeEntry.h"
    2. #include "ArkUICustomContainerNode.h"
    3. #include "ArkUITextNode.h"
    4. #include "UITimer.h"
    5. #include <arkui/native_node_napi.h>
    6. #include <arkui/native_type.h>
    7. #include <js_native_api.h>
    8. namespace NativeModule {
    9. namespace {
    10. napi_env g_env;
    11. } // namespace
    12. napi_value CreateNativeRoot(napi_env env, napi_callback_info info)
    13. {
    14. size_t argc = 1;
    15. napi_value args[1] = {nullptr};
    16. napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    17. ArkUI_NodeContentHandle contentHandle;
    18. OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
    19. NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    20. // 创建自定义容器和文本组件。
    21. auto node = std::make_shared<ArkUICustomContainerNode>();
    22. node->SetBackgroundColor(0xFFE0FFFF);
    23. auto textNode = std::make_shared<ArkUITextNode>();
    24. textNode->SetTextContent("CustomContainer Example");
    25. const int32_t fontSize = 16;
    26. textNode->SetFontSize(fontSize);
    27. textNode->SetBackgroundColor(0xFFfffacd);
    28. textNode->SetTextAlign(ARKUI_TEXT_ALIGNMENT_CENTER);
    29. node->AddChild(textNode);
    30. auto onClick = [](ArkUI_NodeEvent *event) {
    31. auto textNode = (ArkUITextNode *)OH_ArkUI_NodeEvent_GetUserData(event);
    32. textNode->SetFontColor(0xFF00FF7F);
    33. };
    34. textNode->RegisterOnClick(onClick, textNode.get());
    35. // 保持Native侧对象到管理类中,维护生命周期。
    36. NativeEntry::GetInstance()->SetRootNode(node);
    37. g_env = env;
    38. return nullptr;
    39. }
    40. napi_value DestroyNativeRoot(napi_env env, napi_callback_info info)
    41. {
    42. // 从管理类中释放Native侧对象。
    43. NativeEntry::GetInstance()->DisposeRootNode();
    44. return nullptr;
    45. }
    46. } // namespace NativeModule
  4. 修改CMakeLists.txt,添加链接库。

    收起
    自动换行
    深色代码主题
    复制
    1. # CMakeLists.txt
    2. # the minimum version of CMake.
    3. cmake_minimum_required(VERSION 3.4.1)
    4. project(testndk)
    5. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    6. include_directories(${NATIVERENDER_ROOT_PATH}
    7. ${NATIVERENDER_ROOT_PATH}/include)
    8. add_library(entry SHARED NativeEntry.cpp napi_init.cpp)
    9. # target_link_libraries(entry PUBLIC libace_napi.z.so, libace_ndk.z.so, libhilog_ndk.z.so)
    10. find_library(
    11. # Sets the name of the path variable.
    12. hilog-lib
    13. # Specifies the name of the NDK library that
    14. # you want CMake to locate.
    15. hilog_ndk.z
    16. )
    17. find_library(
    18. # Sets the name of the path variable.
    19. libace-lib
    20. # Specifies the name of the NDK library that
    21. # you want CMake to locate.
    22. ace_ndk.z
    23. )
    24. find_library(
    25. # Sets the name of the path variable.
    26. libnapi-lib
    27. # Specifies the name of the NDK library that
    28. # you want CMake to locate.
    29. ace_napi.z
    30. )
    31. find_library(
    32. # Sets the name of the path variable.
    33. libuv-lib
    34. uv
    35. )
    36. target_link_libraries(entry PUBLIC
    37. ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} )

自定义绘制组件

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

图2 自定义绘制组件

  1. 按照自定义布局容器章节准备前置工程。

  2. 创建自定义绘制组件封装对象。

    收起
    自动换行
    深色代码主题
    复制
    1. // ArkUICustomNode.h
    2. // 自定义绘制组件示例
    3. #ifndef MYAPPLICATION_ARKUICUSTOMNODE_H
    4. #define MYAPPLICATION_ARKUICUSTOMNODE_H
    5. #include <native_drawing/drawing_brush.h>
    6. #include <native_drawing/drawing_canvas.h>
    7. #include <native_drawing/drawing_path.h>
    8. #include "ArkUINode.h"
    9. namespace NativeModule {
    10. class ArkUICustomNode : public ArkUINode {
    11. public:
    12. // 使用自定义组件类型ARKUI_NODE_CUSTOM创建组件。
    13. ArkUICustomNode()
    14. : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_CUSTOM))
    15. {
    16. // 注册自定义事件监听器。
    17. nativeModule_->addNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
    18. // 声明自定义事件并传递自身作为自定义数据。
    19. nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW, 0, this);
    20. }
    21. ~ArkUICustomNode() override
    22. {
    23. // 反注册自定义事件监听器。
    24. nativeModule_->removeNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
    25. // 取消声明自定义事件。
    26. nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW);
    27. }
    28. void SetRectColor(uint32_t color)
    29. {
    30. color_ = color;
    31. // 自定义绘制属性变更需要主动通知框架。
    32. nativeModule_->markDirty(handle_, NODE_NEED_RENDER);
    33. }
    34. private:
    35. static void OnStaticCustomEvent(ArkUI_NodeCustomEvent *event)
    36. {
    37. // 获取组件实例对象,调用相关实例方法。
    38. auto customNode = reinterpret_cast<ArkUICustomNode *>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
    39. auto type = OH_ArkUI_NodeCustomEvent_GetEventType(event);
    40. switch (type) {
    41. case ARKUI_NODE_CUSTOM_EVENT_ON_DRAW:
    42. customNode->OnDraw(event);
    43. break;
    44. default:
    45. break;
    46. }
    47. }
    48. // 自定义绘制逻辑。
    49. void OnDraw(ArkUI_NodeCustomEvent *event)
    50. {
    51. auto drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
    52. // 获取图形绘制对象。
    53. auto drawCanvas = reinterpret_cast<OH_Drawing_Canvas *>(OH_ArkUI_DrawContext_GetCanvas(drawContext));
    54. // 获取组件大小。
    55. auto size = OH_ArkUI_DrawContext_GetSize(drawContext);
    56. // 局部资源对象离开作用域时,析构函数会自动分离画笔并释放已经成功创建的资源。
    57. struct DrawingResources {
    58. OH_Drawing_Canvas *canvas = nullptr;
    59. OH_Drawing_Path *path = nullptr;
    60. OH_Drawing_Brush *brush = nullptr;
    61. bool brushAttached = false;
    62. ~DrawingResources()
    63. {
    64. if (brushAttached) {
    65. OH_Drawing_CanvasDetachBrush(canvas);
    66. }
    67. OH_Drawing_BrushDestroy(brush);
    68. OH_Drawing_PathDestroy(path);
    69. }
    70. } resources;
    71. resources.canvas = drawCanvas;
    72. // 绘制自定义内容。
    73. resources.path = OH_Drawing_PathCreate();
    74. if (resources.path == nullptr) {
    75. return;
    76. }
    77. const float kQuarter = 0.25f;
    78. const float kThreeQuarters = 0.75f;
    79. OH_Drawing_PathMoveTo(resources.path, size.width * kQuarter, size.height * kQuarter);
    80. OH_Drawing_PathLineTo(resources.path, size.width * kThreeQuarters, size.height * kQuarter);
    81. OH_Drawing_PathLineTo(
    82. resources.path, size.width * kThreeQuarters, size.height * kThreeQuarters);
    83. OH_Drawing_PathLineTo(resources.path, size.width * kQuarter, size.height * kThreeQuarters);
    84. OH_Drawing_PathLineTo(resources.path, size.width * kQuarter, size.height * kQuarter);
    85. OH_Drawing_PathClose(resources.path);
    86. resources.brush = OH_Drawing_BrushCreate();
    87. if (resources.brush == nullptr) {
    88. return;
    89. }
    90. OH_Drawing_BrushSetColor(resources.brush, color_);
    91. OH_Drawing_CanvasAttachBrush(drawCanvas, resources.brush);
    92. resources.brushAttached = true;
    93. OH_Drawing_CanvasDrawPath(drawCanvas, resources.path);
    94. }
    95. uint32_t color_ = 0xFFFFE4B5;
    96. };
    97. } // namespace NativeModule
    98. #endif // MYAPPLICATION_ARKUICUSTOMNODE_H
  3. 使用自定义绘制组件和自定义容器创建示例界面。

    收起
    自动换行
    深色代码主题
    复制
    1. #include "NativeEntry.h"
    2. #include "ArkUICustomContainerNode.h"
    3. #include "ArkUICustomNode.h"
    4. #include <arkui/native_node_napi.h>
    5. #include <arkui/native_type.h>
    6. #include <js_native_api.h>
    7. #include "UITimer.h"
    8. namespace NativeModule {
    9. namespace {
    10. napi_env g_env;
    11. } // namespace
    12. napi_value CreateNativeRoot(napi_env env, napi_callback_info info)
    13. {
    14. size_t argc = 1;
    15. napi_value args[1] = {nullptr};
    16. napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    17. // 获取NodeContent
    18. ArkUI_NodeContentHandle contentHandle;
    19. OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
    20. NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    21. // 创建自定义容器和自定义绘制组件。
    22. auto node = std::make_shared<ArkUICustomContainerNode>();
    23. node->SetBackgroundColor(0xFFE0FFFF);
    24. auto customNode = std::make_shared<ArkUICustomNode>();
    25. customNode->SetBackgroundColor(0xFFD3D3D3);
    26. const int width = 150;
    27. const int height = 150;
    28. customNode->SetWidth(width);
    29. customNode->SetHeight(height);
    30. node->AddChild(customNode);
    31. auto onClick = [](ArkUI_NodeEvent *event) {
    32. auto customNode = (ArkUICustomNode *)OH_ArkUI_NodeEvent_GetUserData(event);
    33. customNode->SetRectColor(0xFF00FF7F);
    34. };
    35. customNode->RegisterOnClick(onClick, customNode.get());
    36. // 保持Native侧对象到管理类中,维护生命周期。
    37. NativeEntry::GetInstance()->SetRootNode(node);
    38. g_env = env;
    39. return nullptr;
    40. }
    41. napi_value DestroyNativeRoot(napi_env env, napi_callback_info info)
    42. {
    43. // 从管理类中释放Native侧对象。
    44. NativeEntry::GetInstance()->DisposeRootNode();
    45. return nullptr;
    46. }
    47. } // namespace NativeModule
  4. 修改CMakeLists.txt,添加链接库。

    收起
    自动换行
    深色代码主题
    复制
    1. # CMakeLists.txt
    2. # the minimum version of CMake.
    3. cmake_minimum_required(VERSION 3.4.1)
    4. project(testndk)
    5. set(NATIVERENDER_ROOT_PATH ${CMAKE_CURRENT_SOURCE_DIR})
    6. include_directories(${NATIVERENDER_ROOT_PATH}
    7. ${NATIVERENDER_ROOT_PATH}/include)
    8. add_library(entry SHARED NativeEntry.cpp napi_init.cpp)
    9. # target_link_libraries(entry PUBLIC libace_napi.z.so, libace_ndk.z.so, libhilog_ndk.z.so)
    10. find_library(
    11. # Sets the name of the path variable.
    12. hilog-lib
    13. # Specifies the name of the NDK library that
    14. # you want CMake to locate.
    15. hilog_ndk.z
    16. )
    17. find_library(
    18. # Sets the name of the path variable.
    19. libace-lib
    20. # Specifies the name of the NDK library that
    21. # you want CMake to locate.
    22. ace_ndk.z
    23. )
    24. find_library(
    25. # Sets the name of the path variable.
    26. libnapi-lib
    27. # Specifies the name of the NDK library that
    28. # you want CMake to locate.
    29. ace_napi.z
    30. )
    31. find_library(
    32. # Sets the name of the path variable.
    33. libuv-lib
    34. uv
    35. )
    36. target_link_libraries(entry PUBLIC
    37. ${hilog-lib} ${libace-lib} ${libnapi-lib} ${libuv-lib} libnative_drawing.so)

不规则网格布局示例

以下示例创建了一个不规则网格布局容器,支持不同大小的网格单元,实现类似瀑布流的布局效果。完整示例请参考CustomDrawIrregularSample

图3 不规则网格布局效果

  1. 按照自定义布局容器章节准备前置工程。

  2. 创建不规则网格布局容器组件封装对象。

    收起
    自动换行
    深色代码主题
    复制
    1. // ArkUIIrregularGridNode.h
    2. // 不规则网格布局容器示例
    3. #ifndef MYAPPLICATION_ARKUIIRREGULARGRIDNODE_H
    4. #define MYAPPLICATION_ARKUIIRREGULARGRIDNODE_H
    5. #include "ArkUINode.h"
    6. #include <vector>
    7. #include <map>
    8. namespace NativeModule {
    9. // 网格单元配置
    10. struct GridItemConfig {
    11. int32_t rowSpan = 1; // 占据的行数
    12. int32_t columnSpan = 1; // 占据的列数
    13. };
    14. class ArkUIIrregularGridNode : public ArkUINode {
    15. public:
    16. // 使用自定义组件类型ARKUI_NODE_CUSTOM创建组件
    17. ArkUIIrregularGridNode()
    18. : ArkUINode((NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_CUSTOM))
    19. {
    20. // 注册自定义事件监听器
    21. nativeModule_->addNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
    22. // 声明自定义事件并传递自身作为自定义数据
    23. nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE, 0, this);
    24. nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_LAYOUT, 0, this);
    25. }
    26. ~ArkUIIrregularGridNode() override
    27. {
    28. // 反注册自定义事件监听器
    29. nativeModule_->removeNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
    30. // 取消声明自定义事件。
    31. nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE);
    32. nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_LAYOUT);
    33. }
    34. // 设置列数
    35. void SetColumnCount(int32_t count)
    36. {
    37. columnCount_ = count;
    38. nativeModule_->markDirty(handle_, NODE_NEED_MEASURE);
    39. }
    40. // 设置网格间距
    41. void SetGap(int32_t gap)
    42. {
    43. gap_ = gap;
    44. nativeModule_->markDirty(handle_, NODE_NEED_MEASURE);
    45. }
    46. // 设置子组件的网格配置
    47. void SetItemConfig(ArkUI_NodeHandle child, int32_t rowSpan, int32_t columnSpan)
    48. {
    49. GridItemConfig config;
    50. config.rowSpan = rowSpan;
    51. config.columnSpan = columnSpan;
    52. itemConfigs_[child] = config;
    53. nativeModule_->markDirty(handle_, NODE_NEED_MEASURE);
    54. }
    55. private:
    56. static void OnStaticCustomEvent(ArkUI_NodeCustomEvent *event)
    57. {
    58. // 获取组件实例对象,调用相关实例方法。
    59. auto customNode = reinterpret_cast<ArkUIIrregularGridNode *>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
    60. auto type = OH_ArkUI_NodeCustomEvent_GetEventType(event);
    61. switch (type) {
    62. case ARKUI_NODE_CUSTOM_EVENT_ON_MEASURE:
    63. customNode->OnMeasure(event);
    64. break;
    65. case ARKUI_NODE_CUSTOM_EVENT_ON_LAYOUT:
    66. customNode->OnLayout(event);
    67. break;
    68. default:
    69. break;
    70. }
    71. }
    72. // 测算单个子组件并更新列高信息
    73. void MeasureChild(ArkUI_NodeHandle child, int32_t cellWidth,
    74. ArkUI_LayoutConstraint *childConstraint, std::vector<int32_t> &columnHeights)
    75. {
    76. GridItemConfig config = {1, 1};
    77. auto it = itemConfigs_.find(child);
    78. if (it != itemConfigs_.end()) {
    79. config = it->second;
    80. }
    81. if (config.columnSpan > columnCount_) {
    82. config.columnSpan = columnCount_;
    83. }
    84. int32_t startColumn = FindLowestColumn(columnHeights, config.columnSpan);
    85. int32_t startY = 0;
    86. for (int32_t col = startColumn; col < startColumn + config.columnSpan && col < columnCount_; col++) {
    87. if (columnHeights[col] > startY) {
    88. startY = columnHeights[col];
    89. }
    90. }
    91. int32_t itemWidth = cellWidth * config.columnSpan + gap_ * (config.columnSpan - 1);
    92. OH_ArkUI_LayoutConstraint_SetMaxWidth(childConstraint, itemWidth);
    93. OH_ArkUI_LayoutConstraint_SetMinWidth(childConstraint, itemWidth);
    94. nativeModule_->measureNode(child, childConstraint);
    95. auto size = nativeModule_->getMeasuredSize(child);
    96. LayoutItemInfo info;
    97. info.x = startColumn * (cellWidth + gap_);
    98. info.y = startY;
    99. info.width = size.width;
    100. info.height = size.height;
    101. layoutInfo_.push_back(info);
    102. int32_t newHeight = startY + size.height + gap_;
    103. for (int32_t col = startColumn; col < startColumn + config.columnSpan && col < columnCount_; col++) {
    104. columnHeights[col] = newHeight;
    105. }
    106. }
    107. // 自定义测算逻辑:不规则网格布局
    108. void OnMeasure(ArkUI_NodeCustomEvent *event)
    109. {
    110. auto layoutConstrain = OH_ArkUI_NodeCustomEvent_GetLayoutConstraintInMeasure(event);
    111. int32_t maxWidth = OH_ArkUI_LayoutConstraint_GetMaxWidth(layoutConstrain);
    112. int32_t totalGap = gap_ * (columnCount_ - 1);
    113. int32_t cellWidth = (maxWidth - totalGap) / columnCount_;
    114. auto childConstraint = OH_ArkUI_LayoutConstraint_Copy(layoutConstrain);
    115. std::vector<int32_t> columnHeights(columnCount_, 0);
    116. layoutInfo_.clear();
    117. auto totalSize = nativeModule_->getTotalChildCount(handle_);
    118. for (uint32_t i = 0; i < totalSize; i++) {
    119. auto child = nativeModule_->getChildAt(handle_, i);
    120. MeasureChild(child, cellWidth, childConstraint, columnHeights);
    121. }
    122. int32_t maxHeight = 0;
    123. for (int32_t height : columnHeights) {
    124. if (height > maxHeight) {
    125. maxHeight = height;
    126. }
    127. }
    128. if (maxHeight > gap_) {
    129. maxHeight -= gap_;
    130. }
    131. nativeModule_->setMeasuredSize(handle_, maxWidth, maxHeight);
    132. OH_ArkUI_LayoutConstraint_Dispose(childConstraint);
    133. }
    134. void OnLayout(ArkUI_NodeCustomEvent *event)
    135. {
    136. // 获取父组件期望位置并设置
    137. auto position = OH_ArkUI_NodeCustomEvent_GetPositionInLayout(event);
    138. nativeModule_->setLayoutPosition(handle_, position.x, position.y);
    139. // 布局子组件
    140. auto totalSize = nativeModule_->getTotalChildCount(handle_);
    141. for (uint32_t i = 0; i < totalSize && i < layoutInfo_.size(); i++) {
    142. auto child = nativeModule_->getChildAt(handle_, i);
    143. nativeModule_->layoutNode(child, layoutInfo_[i].x, layoutInfo_[i].y);
    144. }
    145. }
    146. // 找到最矮的列,确保可以放下指定列跨度的项
    147. int32_t FindLowestColumn(const std::vector<int32_t>& columnHeights, int32_t columnSpan)
    148. {
    149. int32_t lowestColumn = 0;
    150. int32_t lowestHeight = INT32_MAX;
    151. // 遍历所有可能的起始列
    152. for (int32_t col = 0; col <= columnCount_ - columnSpan; col++) {
    153. // 找到这个范围内最高的列
    154. int32_t maxHeightInRange = 0;
    155. for (int32_t i = col; i < col + columnSpan; i++) {
    156. if (columnHeights[i] > maxHeightInRange) {
    157. maxHeightInRange = columnHeights[i];
    158. }
    159. }
    160. // 如果这个范围的最高点比当前最低点还低,更新最低列
    161. if (maxHeightInRange < lowestHeight) {
    162. lowestHeight = maxHeightInRange;
    163. lowestColumn = col;
    164. }
    165. }
    166. return lowestColumn;
    167. }
    168. struct LayoutItemInfo {
    169. int32_t x;
    170. int32_t y;
    171. int32_t width;
    172. int32_t height;
    173. };
    174. int32_t columnCount_ = 3;
    175. int32_t gap_ = 10;
    176. std::map<ArkUI_NodeHandle, GridItemConfig> itemConfigs_;
    177. std::vector<LayoutItemInfo> layoutInfo_;
    178. };
    179. } // namespace NativeModule
    180. #endif // MYAPPLICATION_ARKUIIRREGULARGRIDNODE_H
  3. 使用不规则网格布局容器创建示例界面。

    收起
    自动换行
    深色代码主题
    复制
    1. #include "NativeEntry.h"
    2. #include "ArkUIIrregularGridNode.h"
    3. #include "ArkUINode.h"
    4. #include <arkui/native_node_napi.h>
    5. #include <arkui/native_type.h>
    6. #include <js_native_api.h>
    7. #include <utility>
    8. #include <vector>
    9. namespace NativeModule {
    10. namespace {
    11. napi_env g_env = nullptr;
    12. constexpr uint32_t GRID_BACKGROUND_COLOR = 0xFFF5F5F5;
    13. constexpr int32_t GRID_COLUMN_COUNT = 4;
    14. constexpr int32_t GRID_GAP = 8;
    15. constexpr float GRID_ITEM_RADIUS = 8.0f;
    16. constexpr float GRID_ITEM_BORDER_WIDTH = 1.0f;
    17. constexpr uint32_t GRID_ITEM_BORDER_COLOR = 0xFFCCCCCC;
    18. constexpr float GRID_ITEM_BASE_HEIGHT = 60.0f;
    19. constexpr float GRID_ITEM_HEIGHT_STEP = 40.0f;
    20. using GridItemSize = std::pair<int32_t, int32_t>;
    21. const std::vector<GridItemSize>& GetGridItemSizes()
    22. {
    23. static const std::vector<GridItemSize> itemSizes = {
    24. {1, 1}, // 小方块
    25. {2, 1}, // 竖长条
    26. {1, 3}, // 横长条
    27. {2, 2}, // 大方块
    28. {1, 1}, // 小方块
    29. {1, 2}, // 横条
    30. {3, 1}, // 很长的竖条
    31. };
    32. return itemSizes;
    33. }
    34. const std::vector<uint32_t>& GetGridItemColors()
    35. {
    36. static const std::vector<uint32_t> colors = {
    37. 0xFF64B5F6, // 蓝色
    38. 0xFFE57373, // 红色
    39. 0xFF81C784, // 绿色
    40. 0xFFFFB74D, // 橙色
    41. 0xFF9575CD, // 紫色
    42. 0xFF4DB6AC, // 青色
    43. 0xFFFFD54F, // 黄色
    44. 0xFFF06292, // 粉色
    45. 0xFF7986CB, // 靛蓝
    46. 0xFFA1887F, // 棕色
    47. };
    48. return colors;
    49. }
    50. void SetNodeColorAttribute(ArkUI_NativeNodeAPI_1* nodeAPI, ArkUI_NodeHandle node, uint32_t color)
    51. {
    52. ArkUI_NumberValue bgColor[] = {{.u32 = color}};
    53. ArkUI_AttributeItem bgColorItem = {bgColor, 1};
    54. nodeAPI->setAttribute(node, NODE_BACKGROUND_COLOR, &bgColorItem);
    55. }
    56. void SetNodeBorderRadiusAttribute(ArkUI_NativeNodeAPI_1* nodeAPI, ArkUI_NodeHandle node, float radius)
    57. {
    58. ArkUI_NumberValue radiusValue[] = {{.f32 = radius}};
    59. ArkUI_AttributeItem radiusItem = {radiusValue, 1};
    60. nodeAPI->setAttribute(node, NODE_BORDER_RADIUS, &radiusItem);
    61. }
    62. void SetNodeBorderStyle(ArkUI_NativeNodeAPI_1* nodeAPI, ArkUI_NodeHandle node)
    63. {
    64. ArkUI_NumberValue borderWidth[] = {{.f32 = GRID_ITEM_BORDER_WIDTH}};
    65. ArkUI_AttributeItem borderWidthItem = {borderWidth, 1};
    66. nodeAPI->setAttribute(node, NODE_BORDER_WIDTH, &borderWidthItem);
    67. ArkUI_NumberValue borderColor[] = {{.u32 = GRID_ITEM_BORDER_COLOR}};
    68. ArkUI_AttributeItem borderColorItem = {borderColor, 1};
    69. nodeAPI->setAttribute(node, NODE_BORDER_COLOR, &borderColorItem);
    70. }
    71. void SetNodeHeightByRowSpan(ArkUI_NativeNodeAPI_1* nodeAPI, ArkUI_NodeHandle node, int32_t rowSpan)
    72. {
    73. float minHeight = GRID_ITEM_BASE_HEIGHT + (rowSpan - 1) * GRID_ITEM_HEIGHT_STEP;
    74. ArkUI_NumberValue minHeightValue[] = {{.f32 = minHeight}};
    75. ArkUI_AttributeItem minHeightItem = {minHeightValue, 1};
    76. nodeAPI->setAttribute(node, NODE_HEIGHT, &minHeightItem);
    77. }
    78. void AddGridItems(
    79. ArkUI_NativeNodeAPI_1* nodeAPI,
    80. const std::shared_ptr<ArkUIIrregularGridNode>& gridNode,
    81. const std::vector<GridItemSize>& itemSizes,
    82. const std::vector<uint32_t>& colors)
    83. {
    84. for (size_t i = 0; i < itemSizes.size(); ++i) {
    85. auto itemNode = nodeAPI->createNode(ARKUI_NODE_STACK);
    86. SetNodeColorAttribute(nodeAPI, itemNode, colors[i % colors.size()]);
    87. SetNodeBorderRadiusAttribute(nodeAPI, itemNode, GRID_ITEM_RADIUS);
    88. SetNodeBorderStyle(nodeAPI, itemNode);
    89. SetNodeHeightByRowSpan(nodeAPI, itemNode, itemSizes[i].first);
    90. gridNode->SetItemConfig(itemNode, itemSizes[i].first, itemSizes[i].second);
    91. nodeAPI->addChild(gridNode->GetHandle(), itemNode);
    92. }
    93. }
    94. } // namespace
    95. napi_value CreateNativeRoot(napi_env env, napi_callback_info info)
    96. {
    97. size_t argc = 1;
    98. napi_value args[1] = {nullptr};
    99. napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    100. ArkUI_NodeContentHandle contentHandle;
    101. OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
    102. NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    103. auto gridNode = std::make_shared<ArkUIIrregularGridNode>();
    104. gridNode->SetBackgroundColor(GRID_BACKGROUND_COLOR);
    105. gridNode->SetColumnCount(GRID_COLUMN_COUNT);
    106. gridNode->SetGap(GRID_GAP);
    107. auto* nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
    108. AddGridItems(nodeAPI, gridNode, GetGridItemSizes(), GetGridItemColors());
    109. // 保持Native侧对象到管理类中,维护生命周期。
    110. NativeEntry::GetInstance()->SetRootNode(gridNode);
    111. g_env = env;
    112. return nullptr;
    113. }
    114. napi_value DestroyNativeRoot(napi_env env, napi_callback_info info)
    115. {
    116. // 从管理类中释放Native侧对象。
    117. NativeEntry::GetInstance()->DisposeRootNode();
    118. return nullptr;
    119. }
    120. } // namespace NativeModule
在 指南 中进行搜索
请输入您想要搜索的关键词