〇、前言

在诸多用户操作中,拖拽操作也是比较常见的,尤其是在使用到图片的应用页面中,那么,在 NDK UI 中该如何对拖拽事件进行捕获呢?下面就进行详细的介绍。

在正式展开本文内容前,不妨先给出官方的Demo 链接,官方使用的是一种脚本式编程方式,而我为了将之前的已实现代码给充分利用起来,将其转化成 NDK UI 模块的编程方式。

实际上,将官方的非工程式 Demo,转化为工程式的 Demo,本身就可以进一步锻炼鸿蒙 NDK UI 开发能力,所以,屏幕前的你如果想提升开发能力,那么我非常建议你跟着我的步骤,实践完成这一转化过程。

一、拖拽相关事件

在之前提到的 ArkUI_NodeEventType 枚举类中,从14到20共7个成员字段,都是与拖拽相关的:
在这里插入图片描述
而在 NDK UI 开发中,但凡需要处理拖拽事件,都会引用到这些枚举量。本文选择以 NODE_ON_DRAG_START 事件为例,演示如何在 NDK UI 中进行拖拽事件的捕获和处理。

二、认识 OH_PixelmapNative

为了让拖拽的操作过程,变得肉眼可察,本文将结合 OH_PixelmapNative 对象,实现一个拖拽动画:
在这里插入图片描述
OH_PixelmapNative 对象,实际上跟 ArkTS 代码中 PixelMap 一样,都是用于定义像素图片的。

而与 OH_PixelmapNative 对象紧密关联的pixelmap_native.h,提供了大量用于设置 OH_PixelmapNative 的 API:
在这里插入图片描述
比如用于初始化 OH_PixelmapNative 对象的 OH_PixelmapInitializationOptions_*** 方法,再比如用于直接操纵 OH_PixelmapNative 对象本身的各种 OH_PixelmapNative_**** 方法:
在这里插入图片描述

三、掌握OH_ArkUI_SetNodeDragPreview

drag_and_drop.h 中,有一个 OH_ArkUI_SetNodeDragPreview API:
在这里插入图片描述
完美地将 OH_PixelmapNative 对象与 UI 节点关联起来,具体就是将 OH_PixelmapNative 对象作为拖拽动作的跟手图在屏幕上显示出来,并且跟手图的具体样式也就是效果,可以通过 ArkUI_DragPreviewOption 进行调整。

ArkUI_DragPreviewOption 是专门用于设置拖拽跟手图的相关自定义参数:
在这里插入图片描述
其相关的 API,就在 drag_and_drop.h 中:
在这里插入图片描述
API 方法名全部都带着 ArkUI_DragPreviewOption,其中的 int32_t OH_ArkUI_SetNodeDragPreviewOption(ArkUI_NodeHandle node, ArkUI_DragPreviewOption* option) 正是用来将 ArkUI_DragPreviewOption 设置到目标 UI 节点上的方法。

四、实现拖拽事件处理

斧头磨好了,现在该开始砍竹子了。

1、RegisterDragStart

之前的 ArkUINode.h 中实现的通用事件注册方法中,并未包含拖拽相关的事件,因此,这里需要补上相关代码:

void RegisterDragStart(const std::function<void(ArkUI_NodeEvent *event)> &onDragStart){
        assert(handle_);
        onDragStart_ = onDragStart;
        nativeModule_->registerNodeEvent(handle_, NODE_ON_DRAG_START, 0, globalParams);
}

别忘了在 ProcessNodeEvent 中添加上 NODE_ON_DRAG_START 的分支:
在这里插入图片描述

2、ArkUIImageNode

新增一个 ArkUIImageNode.h,并在其中实现 Image 节点的创建,和提供相关属性方法:

/*
 * Copyright (c) 2025 彭友聪
 * nativePC is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2. 
 * You may obtain a copy of Mulan PSL v2 at:
            http://license.coscl.org.cn/MulanPSL2 
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, 
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, 
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.  
 * See the Mulan PSL v2 for more details.  
 * 
 * Author: 彭友聪 
 * email:2923616405@qq.com 
 * date: 2025/11/2 07:31
 * file: ${FILE_NAME}
 * product: DevEco Studio
 * LICENSE: MulanPSL-2.0
 * */
//
// Created on 2025/11/2.
//
// Node APIs are not fully supported. To solve the compilation error of the interface cannot be found,
// please include "napi/native_api.h".

#ifndef NATIVEPC_ARKUIIMAGENODE_H
#define NATIVEPC_ARKUIIMAGENODE_H
#include "ArkUINode.h"
#include <arkui/drag_and_drop.h>
namespace NativeModule { 
    class ArkUIImageNode : public ArkUINode {
        public:
            ArkUIImageNode() : ArkUINode(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()->createNode(ARKUI_NODE_IMAGE)){}
			ArkUI_NodeHandle GetHandle() const  {
                return ArkUINode::GetHandle();
            }
			void SetImageSrc(){
				assert(handle_);
				ArkUI_AttributeItem item = {.string = "/../../resources/rawfile/common/logo.png"};
                nativeModule_ -> setAttribute(handle_, NODE_IMAGE_SRC, &item);
			}
            void SetDraggable(bool draggable) {
                assert(handle_);
                OH_ArkUI_SetNodeDraggable(handle_, draggable);
            }
            void SetDragPreview(OH_PixelmapNative *pixelmap){
                assert(handle_);
                OH_ArkUI_SetNodeDragPreview(handle_, pixelmap);
            }
            void SetDragPreviewOption(ArkUI_DragPreviewOption* option){
                assert(handle_);
                OH_ArkUI_SetNodeDragPreviewOption(handle_, option);
            }
    };
}

#endif //NATIVEPC_ARKUIIMAGENODE_H

3、CreateDragImageExample()

准备好前面的代码之后,就可以真正开始编写演示拖拽事件处理的案例代码了。
在这里插入图片描述
整个案例可以分为如下几个部分:
1)节点创建和节点属性设置
2)创建 pixelmap 初始化参数并进行相关设置
3)创建 pixelmap 实例并进行设置
4)将 pixelmap 绑定到节点上作为拖拽跟手图
5)用自定义参数设置拖拽跟手图
6)定义拖拽事件回调函数
7)节点注册拖拽事件并绑定回调函数
8)返回节点

下面,逐步分进行讲解。

3.1、创建 image 节点并设置属性

auto image = std::make_shared<ArkUIImageNode>();
image -> SetWidth(200);
image -> SetHeight(200);
image -> SetBackgroundColor(0xff112233);
image -> SetImageSrc();
image -> SetDraggable(true);

跟之前的案例一样,最开始都是先要将演示载体,也就是 UI 节点给创建出来,否则一切都是空中作画、毫无痕迹。这里使用图像节点作为载体,所以就需要引用上面准备好的 ArkUIImageNode,并通过节点私有属性方法 SetImageSrcSetDraggable,为图像节点设置图像源和可拖拽。
在这里插入图片描述

3.2、定义 OH_Pixelmap_InitializationOptions

由于使用 OH_PixelmapNative_CreatePixelmap 创建 pixelmap 对象时,需要提供一组初始化参数,所以,需要先将对应的初始化参数,也即 OH_Pixelmap_InitializationOptions 对象定义好:

OH_Pixelmap_InitializationOptions *createOpts;
OH_PixelmapInitializationOptions_Create(&createOpts);
OH_PixelmapInitializationOptions_SetWidth(createOpts, 400);
OH_PixelmapInitializationOptions_SetHeight(createOpts, 600);
OH_PixelmapInitializationOptions_SetPixelFormat(createOpts, PIXEL_FORMAT_BGRA_8888);
OH_PixelmapInitializationOptions_SetAlphaType(createOpts, PIXELMAP_ALPHA_TYPE_UNKNOWN);

这里主要定义了 pixelmap 的宽、高和颜色通道。

3.3、创建 Pixelmap 并使用

接下来,开始 Pixelmap 的创建:

uint8_t data[960000];
size_t dataSize = 960000;
for (int i = 0; i < dataSize; i++) {
data[i] = i + 1;
}
OH_PixelmapNative *pixelmap = nullptr;
OH_PixelmapNative_CreatePixelmap(data, dataSize, createOpts, &pixelmap);
OH_PixelmapNative_Rotate(pixelmap, 45);
OH_PixelmapNative_Opacity(pixelmap, 0.1);
OH_PixelmapNative_Scale(pixelmap, 0.5, 1.0);
OH_PixelmapNative_Translate(pixelmap, 50.0, 10.0);

OH_PixelmapNative_CreatePixelmap 方法的声明原型如下:
在这里插入图片描述
方法的第一个参数,用于设置 Pixelmap 的内容,这里为了简单,直接用一个数组替代,不然,就应该用某张图片的真实的颜色数组。Pixelmap 对象创建之后,进行了一组图像变换操作,具体就是设置旋转45度、0.1不透明度、x轴缩小y轴不变和平移。

最后,别忘了用 image -> SetDragPreview(pixelmap);,将创建好的 Pixelmap 绑定到节点上作为跟手图。

3.4、设置跟手图样式

auto *previewOptions = OH_ArkUI_CreateDragPreviewOption();
auto returnValue2 = OH_ArkUI_DragPreviewOption_SetNumberBadgeEnabled(previewOptions, true);
auto returnValue3 = OH_ArkUI_DragPreviewOption_SetBadgeNumber(previewOptions, 10);
auto returnValue4 = OH_ArkUI_DragPreviewOption_SetDefaultRadiusEnabled(previewOptions, true);
image -> SetDragPreviewOption(previewOptions);

通过自定义参数,让跟手图显示数字角标和圆角。

3.5、定义拖拽回调函数

auto dragActionCallback = [](ArkUI_NodeEvent *event) {
  delete globalParams->value;
globalParams->value = new std::string("触发拖拽事件");
globalParams->desc = "触摸Image";
std::string data = globalParams->desc + ": " + *globalParams->value;
napi_env env = g_env; // 获取当前napi环境
napi_value callback;
napi_get_reference_value(env, g_clickCallbackRef, &callback);

// 构造传递参数
napi_value argv;
napi_create_string_utf8(env, data.c_str(), data.length(), &argv);

// 调用ArkTS回调
napi_value result;
napi_call_function(env, nullptr, callback, 1, &argv, &result);
  auto eventType = OH_ArkUI_NodeEvent_GetEventType(event);
  auto targetId = OH_ArkUI_NodeEvent_GetTargetId(event);
  auto *dragEvent = OH_ArkUI_NodeEvent_GetDragEvent(event);
  OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "dragTest",
                "targetId=%{public}d,eventType=%{public}d,", targetId,
                eventType);
  switch (eventType) {
    case NODE_ON_DRAG_START: {
      OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "dragTest",
                    "NODE_ON_DRAG_START EventReceiver");
      OH_UdmfRecord *record = OH_UdmfRecord_Create();
      int returnValue;
      OH_UdsFileUri *imageValue = OH_UdsFileUri_Create();
      returnValue = OH_UdsFileUri_SetFileUri(imageValue, "/pages/common/1111.png");
      returnValue = OH_UdmfRecord_AddFileUri(record, imageValue);
      OH_UdmfData *data = OH_UdmfData_Create();
      returnValue = OH_UdmfData_AddRecord(data, record);
      returnValue = OH_ArkUI_DragEvent_SetData(dragEvent, data);
      break;
    }
    case NODE_ON_DROP: {
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "dragTest",
                      "NODE_ON_DROP EventReceiver");
        // 获取UDMF data
        int returnValue;
        // 创建OH_UdmfData对象
        OH_UdmfData *data = OH_UdmfData_Create();
        returnValue = OH_ArkUI_DragEvent_GetUdmfData(dragEvent, data);
        OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "dragTest",
                      "OH_ArkUI_DragEvent_GetUdmfData returnValue = %{public}d", returnValue);
        // 判断OH_UdmfData是否有对应的类型
        bool resultUdmf = OH_UdmfData_HasType(data, UDMF_META_GENERAL_FILE);
        if (resultUdmf) {
            // 获取OH_UdmfData的记录
            unsigned int recordsCount = 0;
            OH_UdmfRecord **records = OH_UdmfData_GetRecords(data, &recordsCount);
            // 获取records中的元素
            int returnStatus;
            for (int i = 0; i < recordsCount; i++) {
                // 从OH_UdmfRecord中获取文件类型数据
                OH_UdsFileUri *imageValue = OH_UdsFileUri_Create();
                returnStatus = OH_UdmfRecord_GetFileUri(records[i], imageValue);
                const char* fileUri = OH_UdsFileUri_GetFileUri(imageValue);
                OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "dragTest",
                              "dragTest OH_UdmfRecord_GetPlainText "
                              "returnStatus= %{public}d "
                              "fileUri= %{public}s",
                              returnStatus, fileUri);
                // 使用结束后销毁指针
                OH_UdsFileUri_Destroy(imageValue);
            }
        } else {
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "dragTest",
                          "OH_UdmfData_HasType not contain UDMF_META_GENERAL_FILE");
        }
        break;
    }
    default:
            OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "dragTest",
                          "other drag event");
    }
};

这个拖拽事件回调函数中,最开始部分,仍然是进行UI回显。
要让这个回调函数获得执行,需要用 image -> RegisterDragStart(dragActionCallback); 进行注册。

最后,别忘了在 CreateDragImageExample() 方法体末尾,将 image 节点给返回出去。

4、使用 CreateDragImageExample()

打开 NativeEntry.cpp 文件,在 CreateNativeRoot 方法中,对 CreateDragImageExample() 进行调用:
在这里插入图片描述
而真机运行后,日志打印如下:
在这里插入图片描述

五、总结

节点的拖拽事件处理,核心步骤就是事件注册、并绑定回调函数,而回调函数里面就可以编写有关的自定义处理过程,比如 UI 回显等。

这里需要额外说明的是,由于Image节点是在 Native 侧创建的,和之前直接用 ArkTS 代码创建有所不同,尤其是本地图片的使用,如何在c++中成功加载,本文尚未探索出来,这也是为什么前面会有 image -> SetBackgroundColor(0xff112233);这样一行代码。

文末,附上代码仓库

Logo

作为“人工智能6S店”的官方数字引擎,为AI开发者与企业提供一个覆盖软硬件全栈、一站式门户。

更多推荐