目录

自定义绘制

概述

当某些组件本身的绘制内容不满足需求时,可使用组件自定义绘制功能,在原有组件基础上部分绘制、或者全部自行绘制,以达到预期效果。例如:独特的按钮形状、文字和图像混合的图标等。NDK提供了自定义绘制节点的能力,通过自定义绘制事件,开发者可以实现基于NDK侧ArkUI_NodeType中ARKUI_NODE_CUSTOM类型节点的自绘制能力。

自定义绘制层级

自定义绘制提供了五个绘制层级,从低到高依次为:内容背景层(drawBehind)、内容层(drawContent)、内容前景层(drawFront)、前景层(drawForeground)和浮层(drawOverlay)。开发者可以根据需求选择合适的层级进行绘制。自定义绘制层级图如下所示。

开发者可以通过注册相应的事件类型来实现不同层级的自定义绘制,不同层级对应的枚举如下,NDK接口支持的事件类型范围请参考ArkUI_NodeCustomEventType枚举值。

事件类型 说明
ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_BEHIND 自定义内容背景层绘制类型,从API version 20开始支持。
ARKUI_NODE_CUSTOM_EVENT_ON_DRAW 自定义内容层绘制类型。
ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_FRONT 自定义内容前景层绘制类型,从API version 20开始支持。
ARKUI_NODE_CUSTOM_EVENT_ON_FOREGROUND_DRAW 自定义前景层绘制类型。
ARKUI_NODE_CUSTOM_EVENT_ON_OVERLAY_DRAW 自定义浮层绘制类型。

[h2]内容层自定义绘制示例

本示例通过注册内容层绘制事件ARKUI_NODE_CUSTOM_EVENT_ON_DRAW在节点内容层绘制一条从左上区域到右下区域的对角线段,效果图如下。

以下场景基于接入ArkTS页面章节,创建前置工程。内容绘制的完整示例请参考NativeDrawPageSample。

  1. 通过ArkUI_NativeNodeAPI_1的createNode接口,传入ArkUI_NodeType中的ARKUI_NODE_CUSTOM枚举值创建自定义节点。

    auto customNode = nodeAPI->createNode(ARKUI_NODE_CUSTOM);
  2. 事件注册时将自定义节点、事件类型、事件ID和UserData作为参数传入。

    // UserData
    struct A {
        int32_t a = 6;
        bool flag = true;
        ArkUI_NodeHandle node;
    };
    A *a = new A;
    a->node = customNode;
    // ...
    nodeAPI->registerNodeCustomEvent(customNode, ARKUI_NODE_CUSTOM_EVENT_ON_FOREGROUND_DRAW, 1, a);
    // 事件回调函数的编写
    nodeAPI->registerNodeCustomEventReceiver([](ArkUI_NodeCustomEvent *event) {
        // 事件回调函数逻辑
        // ...
    });
  3. 在回调函数中,通过OH_ArkUI_NodeCustomEvent_GetEventType获取自定义事件的事件类型,通过OH_ArkUI_NodeCustomEvent_GetEventTargetId获取事件ID,通过OH_ArkUI_NodeCustomEvent_GetUserData获取UserData,再根据事件类型和事件ID判断当前触发的是哪个绘制事件,从而执行对应的逻辑。

    auto type = OH_ArkUI_NodeCustomEvent_GetEventType(event);
    auto targetId = OH_ArkUI_NodeCustomEvent_GetEventTargetId(event);
    auto userData = reinterpret_cast<A *>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
  4. OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw通过自定义组件事件获取绘制上下文,并将其传入OH_ArkUI_DrawContext_GetCanvas以获取Canvas画布指针,该指针随后将转换为OH_Drawing_Canvas指针进行绘制。

    // 获取自定义事件绘制的上下文。
    auto *drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
    // 获取Canvas指针。
    auto *canvas1 = OH_ArkUI_DrawContext_GetCanvas(drawContext);
    // 转换为OH_Drawing_Canvas指针进行绘制。
    OH_Drawing_Canvas *canvas = reinterpret_cast<OH_Drawing_Canvas *>(canvas1);
    // 绘制逻辑。
    int32_t width = SIZE_1000;  // SIZE_1000 = 1000
    int32_t height = SIZE_1000; // SIZE_1000 = 1000
    auto path = OH_Drawing_PathCreate();
    OH_Drawing_PathMoveTo(path, width / SIZE_4, height / SIZE_4);                   // SIZE_4 = 4
    OH_Drawing_PathLineTo(path, width * SIZE_3 / SIZE_4, height * SIZE_3 / SIZE_4); // SIZE_3 = 3,SIZE_4 = 4
    OH_Drawing_PathClose(path);
    auto pen = OH_Drawing_PenCreate();
    OH_Drawing_PenSetWidth(pen, SIZE_10); // SIZE_10 = 10
    OH_Drawing_PenSetColor(pen, OH_Drawing_ColorSetArgb(0xFF, 0x00, 0x4A, 0x4F));
    OH_Drawing_CanvasAttachPen(canvas, pen);
    OH_Drawing_CanvasDrawPath(canvas, path);

[h2]多层级绘制示例

以下示例创建了一个自定义绘制组件,实现自定义矩形绘制、自定义绘制内容前景层和内容背景层,并支持使用自定义布局容器进行布局排布。完整示例请参考NativeNodeUtilsSample。

图中深蓝矩形为drawFront内容前景层,浅蓝色矩形为drawContent内容层,白色矩形为drawBehind内容背景层。三层的叠加关系用于验证多层级绘制顺序是否符合预期。

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

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

    #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_FRONT, 0, this);
            nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW, 0, this);
            nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_BEHIND, 0, this);
            // 绘制完成事件通知。
            OH_ArkUI_RegisterDrawCallbackOnNodeHandle(handle_, nullptr, [](void* userData) {});
        }
    
        ~ArkUICustomNode() override
        {
            // 反注册自定义事件监听器。
            nativeModule_->removeNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
            // 取消声明自定义事件。
            nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_FRONT);
            nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW);
            nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_BEHIND);
            OH_ArkUI_UnregisterDrawCallbackOnNodeHandle(handle_);
        }
    
    private:
        int32_t NUM_2 = 2;
        int32_t NUM_3 = 3;
        int32_t NUM_4 = 4;
        int32_t NUM_5 = 5;
        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_BEHIND:
                    customNode->OnDrawBehind(event);
                    break;
                case ARKUI_NODE_CUSTOM_EVENT_ON_DRAW:
                    customNode->OnDraw(event);
                    break;
                case ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_FRONT:
                    customNode->OnDrawFront(event);
                    break;
                // ...
                default:
                    break;
            }
        }
    
        // 自定义绘制逻辑。
        void OnDrawBehind(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();
            OH_Drawing_PathMoveTo(path, size.width / NUM_5, size.height / NUM_5);
            OH_Drawing_PathLineTo(path, size.width * NUM_4 / NUM_5, size.height / NUM_5);
            OH_Drawing_PathLineTo(path, size.width * NUM_4 / NUM_5, size.height * NUM_4 / NUM_5);
            OH_Drawing_PathLineTo(path, size.width / NUM_5, size.height * NUM_4 / NUM_5);
            OH_Drawing_PathLineTo(path, size.width / NUM_5, size.height / NUM_5);
            OH_Drawing_PathClose(path);
            auto brush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(brush, 0xFFF0FAFF); // 蓝白色
            OH_Drawing_CanvasAttachBrush(drawCanvas, brush);
            OH_Drawing_CanvasDrawPath(drawCanvas, path);
            // 释放资源
            OH_Drawing_BrushDestroy(brush);
            OH_Drawing_PathDestroy(path);
        }
    
        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();
            OH_Drawing_PathMoveTo(path, size.width / NUM_4, size.height / NUM_4);
            OH_Drawing_PathLineTo(path, size.width * NUM_3 / NUM_4, size.height / NUM_4);
            OH_Drawing_PathLineTo(path, size.width * NUM_3 / NUM_4, size.height * NUM_3 / NUM_4);
            OH_Drawing_PathLineTo(path, size.width / NUM_4, size.height * NUM_3 / NUM_4);
            OH_Drawing_PathLineTo(path, size.width / NUM_4, size.height / NUM_4);
            OH_Drawing_PathClose(path);
            auto brush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(brush, 0xff2787D9); // 浅蓝色
            OH_Drawing_CanvasAttachBrush(drawCanvas, brush);
            OH_Drawing_CanvasDrawPath(drawCanvas, path);
            // 释放资源
            OH_Drawing_BrushDestroy(brush);
            OH_Drawing_PathDestroy(path);
        }
    
        void OnDrawFront(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();
            OH_Drawing_PathMoveTo(path, size.width / NUM_3, size.height / NUM_3);
            OH_Drawing_PathLineTo(path, size.width * NUM_2 / NUM_3, size.height / NUM_3);
            OH_Drawing_PathLineTo(path, size.width * NUM_2 / NUM_3, size.height * NUM_2 / NUM_3);
            OH_Drawing_PathLineTo(path, size.width / NUM_3, size.height * NUM_2 / NUM_3);
            OH_Drawing_PathLineTo(path, size.width / NUM_3, size.height / NUM_3);
            OH_Drawing_PathClose(path);
            auto brush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(brush, 0xFF004AAF); // 深蓝色
            OH_Drawing_CanvasAttachBrush(drawCanvas, brush);
            OH_Drawing_CanvasDrawPath(drawCanvas, path);
            // 释放资源
            OH_Drawing_BrushDestroy(brush);
            OH_Drawing_PathDestroy(path);
        }
        // ...
    };
    
    } // namespace NativeModule
    
    #endif // MYAPPLICATION_ARKUICUSTOMNODE_H
  3. 使用自定义绘制组件和自定义容器创建示例界面。

    #include <arkui/native_node_napi.h>
    #include <arkui/native_type.h>
    #include <js_native_api.h>
    
    #include "NativeEntry.h"
    #include "ArkUICustomContainerNode.h"
    #include "ArkUICustomNode.h"
    #include "ArkUIMessageMaskNode.h"
    
    // 全局环境变量声明
    static napi_env g_env = nullptr;
    // ...
    namespace NativeModule {
    // ...
    #define SIZE_150 150
    // ...
    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(0xFFD5D5D5); // 浅灰色。
        auto customNode = std::make_shared<ArkUICustomNode>();
        customNode->SetBackgroundColor(0xFF707070); // 深灰色。
        customNode->SetWidth(SIZE_150);
        customNode->SetHeight(SIZE_150);
        node->AddChild(customNode);
    
        // 保持Native侧对象到管理类中,维护生命周期。
        NativeEntry::GetInstance()->SetRootNode(node);
        g_env = env;
        return nullptr;
    }
    napi_value CreateNativeMessageRoot(napi_env env, napi_callback_info info)
    {
        constexpr int32_t messageMaskWidth = 400;
        constexpr int32_t messageMaskHeight = 200;
    
        size_t argc = 1;
        napi_value args[1] = {nullptr};
    
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
        // 避免重复创建导致的重复挂载
        NativeEntry::GetInstance()->DisposeRootNode();
    
        // 获取NodeContent
        ArkUI_NodeContentHandle contentHandle;
        OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
        NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    
        auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
        auto rootColumn = std::make_shared<ArkUIColumnNode>();
        auto rootColumnHandle = rootColumn->GetHandle();
        
        // 设置根容器样式
        ArkUI_NumberValue paddingValue[] = {{.f32 = 20.0f}};
        ArkUI_AttributeItem paddingItem = {paddingValue, 1};
        nodeAPI->setAttribute(rootColumnHandle, NODE_PADDING, &paddingItem);
    
        ArkUI_NumberValue bgColorValue[] = {{.u32 = 0xFFFFFFFF}};
        ArkUI_AttributeItem bgColorItem = {bgColorValue, 1};
        nodeAPI->setAttribute(rootColumnHandle, NODE_BACKGROUND_COLOR, &bgColorItem);
        
        // 创建消息气泡组件
        auto maskNode = std::make_shared<ArkUIMessageMaskNode>();
        maskNode->SetWidth(messageMaskWidth);
        maskNode->SetHeight(messageMaskHeight);
        maskNode->SetMessage("您有一条新消息");
        maskNode->SetMaskVisible(false);  // 初始不显示蒙层
        
        // 创建按钮用于切换蒙层效果
        auto buttonNode = std::make_shared<ArkUINode>(nodeAPI->createNode(ARKUI_NODE_BUTTON));
        auto buttonHandle = buttonNode->GetHandle();
        
        // 设置按钮文本
        ArkUI_AttributeItem labelItem;
        const char* buttonLabel = "切换蒙层效果";
        labelItem.string = buttonLabel;
        nodeAPI->setAttribute(buttonHandle, NODE_BUTTON_LABEL, &labelItem);
        
        // 设置按钮样式
        ArkUI_NumberValue marginValue[] = {{.f32 = 20.0f}};
        ArkUI_AttributeItem marginItem = {marginValue, 1};
        nodeAPI->setAttribute(buttonHandle, NODE_MARGIN, &marginItem);
        
        ArkUI_NumberValue btnBgColorValue[] = {{.u32 = 0xFF2787D9}};
        ArkUI_AttributeItem btnBgColorItem = {btnBgColorValue, 1};
        nodeAPI->setAttribute(buttonHandle, NODE_BACKGROUND_COLOR, &btnBgColorItem);
    
        // 设置按钮点击事件
        auto onClick = [](ArkUI_NodeEvent *event) {
            auto maskNode = (ArkUIMessageMaskNode *)OH_ArkUI_NodeEvent_GetUserData(event);
            static bool highlighted = false;
            highlighted = !highlighted;
            maskNode->SetMaskVisible(highlighted);
        };
        buttonNode->RegisterOnClick(onClick, maskNode.get());
        
        // 将组件添加到根容器
        rootColumn->AddChild(buttonNode);
        rootColumn->AddChild(maskNode);
    
        // 保持Native侧对象到管理类中,维护生命周期
        NativeEntry::GetInstance()->SetRootNode(rootColumn);
        return nullptr;
    }
    
    napi_value DestroyNativeRoot(napi_env env, napi_callback_info info)
    {
        // 从管理类中释放Native侧对象。
        NativeEntry::GetInstance()->DisposeRootNode();
        return nullptr;
    }
    } // namespace NativeModule

通过前景绘制实现消息蒙层

以下示例创建了一个消息提示组件,通过内容层绘制消息气泡与文本,并在前景层叠加星标装饰,实现消息高亮提示效果,常用于消息提醒和引导标记等场景。完整示例请参考NativeNodeUtilsSample。

未添加消息蒙层,没有前景层叠加星标装饰效果:

添加消息蒙层,添加后有前景层叠加星标装饰效果:

  1. 按照多层级绘制示例章节准备前置工程。

  2. 创建消息蒙层组件封装对象。

    #ifndef MYAPPLICATION_ARKUIMESSAGEMASKNODE_H
    #define MYAPPLICATION_ARKUIMESSAGEMASKNODE_H
    
    #include <cmath>
    #include <native_drawing/drawing_brush.h>
    #include <native_drawing/drawing_canvas.h>
    #include <native_drawing/drawing_color_filter.h>
    #include <native_drawing/drawing_font.h>
    #include <native_drawing/drawing_font_collection.h>
    #include <native_drawing/drawing_path.h>
    #include <native_drawing/drawing_pen.h>
    #include <native_drawing/drawing_point.h>
    #include <native_drawing/drawing_rect.h>
    #include <native_drawing/drawing_round_rect.h>
    #include <native_drawing/drawing_text_typography.h>
    
    #include <string>
    
    #include "ArkUINode.h"
    
    namespace NativeModule {
    class ArkUIMessageMaskNode : public ArkUINode {
    public:
        // 使用自定义组件类型ARKUI_NODE_CUSTOM创建组件
        ArkUIMessageMaskNode()
            : ArkUINode(
                  (NativeModuleInstance::GetInstance()->GetNativeNodeAPI())->createNode(ARKUI_NODE_CUSTOM))
        {
            // 注册自定义事件监听器
            nativeModule_->addNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
            // 声明自定义事件并传递自身作为自定义数据
            nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_FRONT, 0, this);
            nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW, 0, this);
            nativeModule_->registerNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_BEHIND, 0, this);
        }
    
        ~ArkUIMessageMaskNode() override
        {
            // 反注册自定义事件监听器
            nativeModule_->removeNodeCustomEventReceiver(handle_, OnStaticCustomEvent);
            // 取消声明自定义事件
            nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_FRONT);
            nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW);
            nativeModule_->unregisterNodeCustomEvent(handle_, ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_BEHIND);
        }
    
        // 设置消息文本
        void SetMessage(const std::string& message)
        {
            message_ = message;
            nativeModule_->markDirty(handle_, NODE_NEED_RENDER);
        }
    
        // 设置是否显示蒙层
        void SetMaskVisible(bool visible)
        {
            maskVisible_ = visible;
            nativeModule_->markDirty(handle_, NODE_NEED_RENDER);
        }
    
    private:
        static constexpr int starDecorationCount = 3;
        static constexpr int starPointCount = 5;
        static constexpr float starStartAngleDegrees = -90.0f;
        static constexpr float starAngleStepDegrees = 72.0f;
        static constexpr float starInnerAngleOffsetDegrees = 36.0f;
        static constexpr float starInnerRadiusRatio = 0.4f;
        static constexpr float degreeToRadian = 3.14159265f / 180.0f;
        static constexpr float messageTextFontSize = 23.0f;
    
        static void OnStaticCustomEvent(ArkUI_NodeCustomEvent* event)
        {
            auto customNode = reinterpret_cast<ArkUIMessageMaskNode*>(OH_ArkUI_NodeCustomEvent_GetUserData(event));
            auto type = OH_ArkUI_NodeCustomEvent_GetEventType(event);
            switch (type) {
                case ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_BEHIND:
                    customNode->OnDrawBehind(event);
                    break;
                case ARKUI_NODE_CUSTOM_EVENT_ON_DRAW:
                    customNode->OnDraw(event);
                    break;
                case ARKUI_NODE_CUSTOM_EVENT_ON_DRAW_FRONT:
                    customNode->OnDrawFront(event);
                    break;
                default:
                    break;
            }
        }
    
        // 自定义内容层背景:绘制聊天界面背景
        void OnDrawBehind(ArkUI_NodeCustomEvent* event)
        {
            auto drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
            auto canvas = reinterpret_cast<OH_Drawing_Canvas*>(OH_ArkUI_DrawContext_GetCanvas(drawContext));
            auto size = OH_ArkUI_DrawContext_GetSize(drawContext);
    
            // 绘制浅灰色背景
            auto bgRect = OH_Drawing_RectCreate(0, 0, size.width, size.height);
            auto brush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(brush, 0xFFF5F5F5);
            OH_Drawing_CanvasAttachBrush(canvas, brush);
            OH_Drawing_CanvasDrawRect(canvas, bgRect);
            OH_Drawing_CanvasDetachBrush(canvas);
            OH_Drawing_BrushDestroy(brush);
            OH_Drawing_RectDestroy(bgRect);
        }
    
        // 自定义内容层
        void OnDraw(ArkUI_NodeCustomEvent* event)
        {
            auto drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
            auto canvas = reinterpret_cast<OH_Drawing_Canvas*>(OH_ArkUI_DrawContext_GetCanvas(drawContext));
            auto size = OH_ArkUI_DrawContext_GetSize(drawContext);
    
            float padding = 50.0f;
            float bubbleWidth = size.width - 2 * padding;
            float bubbleHeight = size.height - 2 * padding;
            float cornerRadius = 8.0f;
            float textX = padding + 15.0f;
            float textY = padding + 20.0f;
            float textMaxWidth = bubbleWidth - 30.0f;
    
            // 绘制气泡阴影
            auto shadowRect = OH_Drawing_RectCreate(
                padding + 2, padding + 2, padding + bubbleWidth + 2, padding + bubbleHeight + 2);
            auto* shadowRoundRect = OH_Drawing_RoundRectCreate(shadowRect, cornerRadius, cornerRadius);
            auto shadowBrush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(shadowBrush, 0x30000000);
            OH_Drawing_CanvasAttachBrush(canvas, shadowBrush);
            OH_Drawing_CanvasDrawRoundRect(canvas, shadowRoundRect);
            OH_Drawing_CanvasDetachBrush(canvas);
            OH_Drawing_BrushDestroy(shadowBrush);
            OH_Drawing_RoundRectDestroy(shadowRoundRect);
            OH_Drawing_RectDestroy(shadowRect);
    
            // 绘制绿色气泡背景
            auto bubbleRect = OH_Drawing_RectCreate(padding, padding, padding + bubbleWidth, padding + bubbleHeight);
            auto* bubbleRoundRect = OH_Drawing_RoundRectCreate(bubbleRect, cornerRadius, cornerRadius);
            auto bubbleBrush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(bubbleBrush, 0xFF95EC69);
            OH_Drawing_CanvasAttachBrush(canvas, bubbleBrush);
            OH_Drawing_CanvasDrawRoundRect(canvas, bubbleRoundRect);
            OH_Drawing_CanvasDetachBrush(canvas);
            OH_Drawing_BrushDestroy(bubbleBrush);
    
            // 绘制气泡边框
            auto pen = OH_Drawing_PenCreate();
            OH_Drawing_PenSetWidth(pen, 1.0f);
            OH_Drawing_PenSetColor(pen, 0xFF7FD65A);
            OH_Drawing_CanvasAttachPen(canvas, pen);
            OH_Drawing_CanvasDrawRoundRect(canvas, bubbleRoundRect);
            OH_Drawing_CanvasDetachPen(canvas);
            OH_Drawing_PenDestroy(pen);
            OH_Drawing_RoundRectDestroy(bubbleRoundRect);
            OH_Drawing_RectDestroy(bubbleRect);
    
            // 绘制消息文本
            DrawMessageText(canvas, textX, textY, textMaxWidth);
        }
    
        // 自定义内容前景层:绘制装饰性蒙层
        void OnDrawFront(ArkUI_NodeCustomEvent* event)
        {
            if (!maskVisible_) {
                return;
            }
    
            auto drawContext = OH_ArkUI_NodeCustomEvent_GetDrawContextInDraw(event);
            auto canvas = reinterpret_cast<OH_Drawing_Canvas*>(OH_ArkUI_DrawContext_GetCanvas(drawContext));
    
            float padding = 50.0f;
    
            auto starBrush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(starBrush, 0x88FFFFFF);
            OH_Drawing_CanvasAttachBrush(canvas, starBrush);
    
            const float starRadius = 13.0f;
            const float textLeftX = padding + 26.0f;
            const float textRightX = padding + 146.0f;
            const float starBottomY = padding + 50.0f;
            const float starTopY = padding + 10.0f;
            for (int i = 0; i < starDecorationCount; ++i) {
                float t = static_cast<float>(i) / static_cast<float>(starDecorationCount - 1);
                float starX = textLeftX + (textRightX - textLeftX) * t;
                float starY = starBottomY + (starTopY - starBottomY) * t;
                DrawStar(canvas, starX, starY, starRadius);
            }
    
            OH_Drawing_CanvasDetachBrush(canvas);
            OH_Drawing_BrushDestroy(starBrush);
        }
    
        // 绘制五角星
        void DrawStar(OH_Drawing_Canvas* canvas, float cx, float cy, float radius)
        {
            auto path = OH_Drawing_PathCreate();
            for (int i = 0; i < starPointCount; ++i) {
                float angle = starStartAngleDegrees + i * starAngleStepDegrees;
                float rad = angle * degreeToRadian;
                float x = cx + radius * std::cos(rad);
                float y = cy + radius * std::sin(rad);
                if (i == 0) {
                    OH_Drawing_PathMoveTo(path, x, y);
                } else {
                    OH_Drawing_PathLineTo(path, x, y);
                }
    
                // 内角点
                float innerAngle = angle + starInnerAngleOffsetDegrees;
                float innerRad = innerAngle * degreeToRadian;
                float innerX = cx + radius * starInnerRadiusRatio * std::cos(innerRad);
                float innerY = cy + radius * starInnerRadiusRatio * std::sin(innerRad);
                OH_Drawing_PathLineTo(path, innerX, innerY);
            }
    
            OH_Drawing_PathClose(path);
            OH_Drawing_CanvasDrawPath(canvas, path);
            OH_Drawing_PathDestroy(path);
        }
    
        // 绘制消息文本
        void DrawMessageText(OH_Drawing_Canvas* canvas, float x, float y, float maxWidth)
        {
            // 创建字体集合
            auto* fontCollection = OH_Drawing_CreateFontCollection();
    
            // 创建排版样式
            auto* typographyStyle = OH_Drawing_CreateTypographyStyle();
            OH_Drawing_SetTypographyTextAlign(typographyStyle, TEXT_ALIGN_LEFT);
    
            // 创建排版处理器
            auto* typographyHandler = OH_Drawing_CreateTypographyHandler(typographyStyle, fontCollection);
    
            // 创建文本样式
            auto* textStyle = OH_Drawing_CreateTextStyle();
            OH_Drawing_SetTextStyleColor(textStyle, 0xFF000000); // 纯黑
            OH_Drawing_SetTextStyleFontSize(textStyle, messageTextFontSize);
            OH_Drawing_SetTextStyleFontWeight(textStyle, FONT_WEIGHT_400);
            auto textBrush = OH_Drawing_BrushCreate();
            OH_Drawing_BrushSetColor(textBrush, 0xFF000000);
            OH_Drawing_SetTextStyleForegroundBrush(textStyle, textBrush);
    
            // 添加文本
            OH_Drawing_TypographyHandlerPushTextStyle(typographyHandler, textStyle);
            OH_Drawing_TypographyHandlerAddText(typographyHandler, message_.c_str());
            OH_Drawing_TypographyHandlerPopTextStyle(typographyHandler);
    
            // 创建排版对象并绘制
            auto* typography = OH_Drawing_CreateTypography(typographyHandler);
            OH_Drawing_TypographyLayout(typography, maxWidth);
            OH_Drawing_TypographyPaint(typography, canvas, x, y);
    
            // 释放资源
            OH_Drawing_DestroyTextStyle(textStyle);
            OH_Drawing_DestroyTypography(typography);
            OH_Drawing_DestroyTypographyHandler(typographyHandler);
            OH_Drawing_DestroyTypographyStyle(typographyStyle);
            OH_Drawing_DestroyFontCollection(fontCollection);
            OH_Drawing_BrushDestroy(textBrush);
        }
    
        std::string message_ = "这是一条消息提示";
        bool maskVisible_ = false;
    };
    } // namespace NativeModule
    
    #endif // MYAPPLICATION_ARKUIMESSAGEMASKNODE_H
  3. 使用消息蒙层组件创建示例界面。

    #include <arkui/native_node_napi.h>
    #include <arkui/native_type.h>
    #include <js_native_api.h>
    
    #include "NativeEntry.h"
    #include "ArkUICustomContainerNode.h"
    #include "ArkUICustomNode.h"
    #include "ArkUIMessageMaskNode.h"
    
    // 全局环境变量声明
    static napi_env g_env = nullptr;
    // ...
    namespace NativeModule {
    // ...
    napi_value CreateNativeMessageRoot(napi_env env, napi_callback_info info)
    {
        constexpr int32_t messageMaskWidth = 400;
        constexpr int32_t messageMaskHeight = 200;
    
        size_t argc = 1;
        napi_value args[1] = {nullptr};
    
        napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
        // 避免重复创建导致的重复挂载
        NativeEntry::GetInstance()->DisposeRootNode();
    
        // 获取NodeContent
        ArkUI_NodeContentHandle contentHandle;
        OH_ArkUI_GetNodeContentFromNapiValue(env, args[0], &contentHandle);
        NativeEntry::GetInstance()->SetContentHandle(contentHandle);
    
        auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();
        auto rootColumn = std::make_shared<ArkUIColumnNode>();
        auto rootColumnHandle = rootColumn->GetHandle();
        
        // 设置根容器样式
        ArkUI_NumberValue paddingValue[] = {{.f32 = 20.0f}};
        ArkUI_AttributeItem paddingItem = {paddingValue, 1};
        nodeAPI->setAttribute(rootColumnHandle, NODE_PADDING, &paddingItem);
    
        ArkUI_NumberValue bgColorValue[] = {{.u32 = 0xFFFFFFFF}};
        ArkUI_AttributeItem bgColorItem = {bgColorValue, 1};
        nodeAPI->setAttribute(rootColumnHandle, NODE_BACKGROUND_COLOR, &bgColorItem);
        
        // 创建消息气泡组件
        auto maskNode = std::make_shared<ArkUIMessageMaskNode>();
        maskNode->SetWidth(messageMaskWidth);
        maskNode->SetHeight(messageMaskHeight);
        maskNode->SetMessage("您有一条新消息");
        maskNode->SetMaskVisible(false);  // 初始不显示蒙层
        
        // 创建按钮用于切换蒙层效果
        auto buttonNode = std::make_shared<ArkUINode>(nodeAPI->createNode(ARKUI_NODE_BUTTON));
        auto buttonHandle = buttonNode->GetHandle();
        
        // 设置按钮文本
        ArkUI_AttributeItem labelItem;
        const char* buttonLabel = "切换蒙层效果";
        labelItem.string = buttonLabel;
        nodeAPI->setAttribute(buttonHandle, NODE_BUTTON_LABEL, &labelItem);
        
        // 设置按钮样式
        ArkUI_NumberValue marginValue[] = {{.f32 = 20.0f}};
        ArkUI_AttributeItem marginItem = {marginValue, 1};
        nodeAPI->setAttribute(buttonHandle, NODE_MARGIN, &marginItem);
        
        ArkUI_NumberValue btnBgColorValue[] = {{.u32 = 0xFF2787D9}};
        ArkUI_AttributeItem btnBgColorItem = {btnBgColorValue, 1};
        nodeAPI->setAttribute(buttonHandle, NODE_BACKGROUND_COLOR, &btnBgColorItem);
    
        // 设置按钮点击事件
        auto onClick = [](ArkUI_NodeEvent *event) {
            auto maskNode = (ArkUIMessageMaskNode *)OH_ArkUI_NodeEvent_GetUserData(event);
            static bool highlighted = false;
            highlighted = !highlighted;
            maskNode->SetMaskVisible(highlighted);
        };
        buttonNode->RegisterOnClick(onClick, maskNode.get());
        
        // 将组件添加到根容器
        rootColumn->AddChild(buttonNode);
        rootColumn->AddChild(maskNode);
    
        // 保持Native侧对象到管理类中,维护生命周期
        NativeEntry::GetInstance()->SetRootNode(rootColumn);
        return nullptr;
    }
    
    napi_value DestroyNativeRoot(napi_env env, napi_callback_info info)
    {
        // 从管理类中释放Native侧对象。
        NativeEntry::GetInstance()->DisposeRootNode();
        return nullptr;
    }
    } // namespace NativeModule