鸿蒙原生系列之拖拽事件
本文介绍了在鸿蒙NDK UI开发中实现拖拽事件处理的方法。
NDK UI 拖拽事件
〇、前言
在诸多用户操作中,拖拽操作也是比较常见的,尤其是在使用到图片的应用页面中,那么,在 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,并通过节点私有属性方法 SetImageSrc 和 SetDraggable,为图像节点设置图像源和可拖拽。
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);这样一行代码。
文末,附上代码仓库。
更多推荐


所有评论(0)