当前位置: 首页 > news >正文

鸿蒙原生系列之手势事件自定义处理

手势事件自定义处理

  • 〇、前言
  • 一、认识 native_gesture.h
    • 1、手势类型
    • 2、识别模式
  • 二、代码实践
    • 1、定义 Column 组件
    • 2、准备代码框架
    • 3、创建 Column 节点
    • 4、实现手势事件自定义处理
      • 4.1、必要的判断代码
      • 4.2、创建手势组
      • 4.3、创建长按手势及其回调函数
      • 4.4、创建快滑手势及其回调函数
      • 4.5、绑定手势组
      • 4.6、手势触发说明

〇、前言

时间截止到本文发布为止,我已经顺利鸟枪换大炮,鸿蒙PC的系统顺利升级到6.x beta 版,甚至为鸿蒙PC量身定做的 DevEco Studio 鸿蒙预览版,也已经获得测试资格并开始了尝鲜使用。系统的升级,让我可以继续体验鸿蒙 API 的新特性,上一篇的案例也顺利在升级系统后的鸿蒙PC上完成了演示,接下来,继续深入探索鸿蒙 NDK UI 的相关 API。

点击,是用户与应用进行交互的方式之一,此外,在具有触摸屏的终端设备上,用手指在屏幕上长按或滑动,也是场景的交互方式,恰好,我的鸿蒙PC的屏幕也是支持触摸操作的,换言之,我的鸿蒙PC具备演示手势事件自定义处理功能的条件,下面就让我用理论和实践相结合的方式,为大家分享如何在鸿蒙 NDK UI 中完成手势事件的自定义处理。

一、认识 native_gesture.h

在这里插入图片描述
在鸿蒙开发者官网的API参考文档中,ArkUI(方舟UI框架)一章的目录下,已经专门开辟了 C API 的相关内容。在 C API 目录下,展开头文件节点,仔细浏览文档标题,就会发现一个 native_gesture.h 的文档,而这个就是我们在鸿蒙 NDK UI 中实现手势事件自定义处理功能,所必需参考和阅读的官方文档。

1、手势类型

native_gesture.h 中定义了很多结构体:
在这里插入图片描述
其中一个名为ArkUI_NativeGestureAPI_1的结构体中,定义了当前鸿蒙NDK UI 所支持的手势类型:
在这里插入图片描述
分别是:
1)点击手势:Tap Gesture
2)长按手势:Long Press Gesture
3)滑动手势:Pan Gesture
4)捏合手势:Pinch Gesture
5)旋转手势:Rotation Gesture
6)快滑手势:Swipe Gesture

这六种手势各自对应着一种手指在触摸屏上的操作,称之为单一手势;单一手势允许按照指定的逻辑进行组合,也就是组合手势。在进行手势的具体处理逻辑中,通常都会采取组合的方式。

2、识别模式

组合手势是通过将两个及其以上的多个单一手势进行组合来实现的,由于涉及到多个单一手势,就出现了一个手势识别的先后顺序该怎么处理的问题,这种识别顺序如果处理不好就容易给用户带来不好的体验,因此,是每一个想要实现手势自定义处理功能的开发者,都必须学会和掌握的。

针对这种场景,native_gesture.h 中专门定义了一个枚举类:ArkUI_GroupGestureMode 用于设置组合手势的识别模式,也即设置不同单一手势的先后处理顺序:
在这里插入图片描述
1)SEQUENTIAL_GROUP:顺序识别,按照注册顺序识别手势,直到所有手势识别成功。若有识别失败,后续识别均失败。仅有最后一个手势响应结束事件。
2)PARALLEL_GROUP:并行识别,注册的手势同时识别,直到所有手势识别结束,手势识别互相不影响。
3)EXCLUSIVE_GROUP:互斥识别,注册的手势同时识别,若有一个手势识别成功,则结束手势识别。

顺序识别模式,相当于一段条件语句精心设置的if…elif…else语句;而并行识别,就相当于多个独立的 if 语句;而互斥识别,更像是 switch 语句。

二、代码实践

1、定义 Column 组件

比起列表组件,块布局的 Column 组件,更适合作为用来体验手势捕获和处理的载体,因此,我们需要将之前实现的NDK UI 的根节点替换为 Column 组件:
在这里插入图片描述
设置根节点的代码,就在 NativeEntry.cpp文件的 CreateNativeRoot 方法中,不过,在进行这一修改操作前,不妨先新建一个 ArkUIColumnNode.h文件,在其中编写如下代码:

/** 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/10/26 10:15* file: ${FILE_NAME}* product: DevEco Studio* LICENSE: MulanPSL-2.0* */
//
// Created on 2025/10/26.
//
// 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_ARKUICOLUMNNODE_H
#define NATIVEPC_ARKUICOLUMNNODE_H
#include "ArkUINode.h"
namespace NativeModule { class ArkUIColumnNode : public ArkUINode {public:ArkUIColumnNode() : ArkUINode(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()->createNode(ARKUI_NODE_COLUMN)){}ArkUI_NodeHandle GetHandle() const  {return ArkUINode::GetHandle();}};
}#endif //NATIVEPC_ARKUICOLUMNNODE_H

用于创建 Column 节点。

如果屏幕前的你,是从本系列第一篇开始看的,并且也认真对比了此前已经实现的各种鸿蒙 NDK UI 节点,你就会发现,想要创建对应类型的组件节点,只需将 ArkUINode(NativeModuleInstance::GetInstance()->GetNativeNodeAPI()->createNode(<[ArkUI_NodeType]>)) 的 ArkUI_NodeType 替换为具体的组件类型即可,意识到这一点,你便顺利地获得一次举一反三的思考成就。

2、准备代码框架

在编写有关手势处理的代码前,先将用于承接手势操作的组件——Column 组件创建出来,为此,新建一个 TestGestureExample.h 文件,先在里面完成一个基本框架:

namespace NativeModule {std::shared_ptr<ArkUIBaseNode> testGestureExample() {}
}

为了方便调用此前封装好的相关方法,演示手势操作的 testGestureExample() 同样地放在 NativeModule 中,就像之前的 CreateTextListExample() 方法一样。

3、创建 Column 节点

接着,在 testGestureExample() 中仿造 CreateTextListExample() 创建列表节点的代码,创建一个 Column 节点:

// 创建 Column 节点
std::shared_ptr<ArkUIBaseNode> testGestureExample() {
auto column = std::make_shared<ArkUIColumnNode>();
column->SetBackgroundColor(0xff112233);
column->SetHeight(200);
column->SetWidth(200);
// .....
// 处理手势的代码
// ....
return column;

4、实现手势事件自定义处理

最后,在testGestureExample()方法体的中间位置,完成手势事件的自定义处理。

4.1、必要的判断代码

就像前面所说的那样,手势是触摸屏上的特有操作,而非触摸屏上用鼠标是很难模拟出来的,因此,在进行真正的手势事件处理前,需要判断运行当前程序的设备上是否支持手势:

// 判断是否支持创建手势auto gestureApi = reinterpret_cast<ArkUI_NativeGestureAPI_1 *>(OH_ArkUI_QueryModuleInterfaceByName(ARKUI_NATIVE_GESTURE, "ArkUI_NativeGestureAPI_1"));if (gestureApi->createGroupGesture) {OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager","GestureSampleLog, createGroupGesture api exist");// 具体的手势处理代码.......return column;} else {OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager","GestureSampleLog, createGroupGesture api not exist");return column;}

由于判断分支的引入,之前的 return 语句不得不随之变动;在支持手势操作的分支里面,返回 column 组件之前,会将手势处理的相关逻辑跟 column 组件关联起来,而另一分支中,则是直接将 column 组件返回,如此,才不会在不支持手势操作的设备上引发严重的程序异常。

4.2、创建手势组

本文直接以手势组为案例进行手势处理的演示,因为,创建手势组就必须要创建单一手势,所以,就没必要搞一个 Demo 去演示单一手势的创建。此外,在本文的案例中,手势组的识别模式将被设置为顺序模式,想体验其他识别模式的,不妨根据后面的说明去自行体验。

创建手势组的代码很简单,就一行代码:

// 创建手势组,并设置识别模式为顺序识别
auto groupGesture = gestureApi->createGroupGesture(ArkUI_GroupGestureMode::SEQUENTIAL_GROUP);

1)顺序识别:createGroupGesture 传入 ArkUI_GroupGestureMode::SEQUENTIAL_GROUP
2)并行识别:createGroupGesture 传入 ArkUI_GroupGestureMode::PARALLEL_GROUP
3)互斥识别:createGroupGesture 传入 ArkUI_GroupGestureMode::EXCLUSIVE_GROUP

4.3、创建长按手势及其回调函数

有了手势组后,可以开始创建用于注册到手势组中的单一手势,这里先创建一个长按手势,并编写其对应的回调函数,也就是处理长按手势事件的代码。

// 获取触发该事件的组件对象
auto nodeHandle = column->GetHandle();
if (nodeHandle == nullptr) {return nullptr;
}
// 创建长按手势
auto longPressGesture = gestureApi->createLongPressGesture(1, true, 500);
if (gestureApi->getGestureType) {ArkUI_GestureRecognizerType type = gestureApi->getGestureType(longPressGesture);OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager","GestureSampleLog longPressGesture,ArkUI_GestureRecognizerType%{public}d", type);
}
// 给长按手势定回调
auto onActionCallBackPanLongPress = [](ArkUI_GestureEvent *event, void *extraParam) {delete globalParams->value;globalParams->value = new std::string("触发长按事件");globalParams->desc = "触摸column";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);ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);float velocity = OH_ArkUI_PanGesture_GetVelocity(event);float velocityX = OH_ArkUI_PanGesture_GetVelocityX(event);float velocityY = OH_ArkUI_PanGesture_GetVelocityY(event);float offsetX = OH_ArkUI_PanGesture_GetOffsetX(event);float offsetY = OH_ArkUI_PanGesture_GetOffsetY(event);float scale = OH_ArkUI_PinchGesture_GetScale(event);float centerX = OH_ArkUI_PinchGesture_GetCenterX(event);float centerY = OH_ArkUI_PinchGesture_GetCenterY(event);float angle = OH_ArkUI_SwipeGesture_GetAngle(event);float VelocityS = OH_ArkUI_SwipeGesture_GetVelocity(event);float angleR = OH_ArkUI_RotationGesture_GetAngle(event);float repeat = OH_ArkUI_LongPress_GetRepeatCount(event);OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager","GestureSampleLog,longPressGesturecallback actionType:%{public}d,velocity%{public}f,velocityX""%{public}f;""velocityY%{public}f,offsetX%{public}f,offsetY%{public}f,scale%{public}fcenterX""%{public}fcenterY""%{public}fangle%{public}fVelocityS%{public}fangleR%{public}frepeat%{public}f",actionType, velocity, velocityX, velocityY, offsetX, offsetY, scale, centerX, centerY, angle, VelocityS,angleR, repeat);
};
gestureApi->setGestureEventTarget(longPressGesture,GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE | GESTURE_EVENT_ACTION_CANCEL,nodeHandle, onActionCallBackPanLongPress);// 将长按手势添加到手势组
if (gestureApi->addChildGesture) {gestureApi->addChildGesture(groupGesture, longPressGesture);OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager", "GestureSampleLog, addChildGesture longPressGesture");
}

由于手势处理代码,必须用setGestureEventTarget绑定到 Column 组件身上才行,而 setGestureEventTarget 的第三个参数,即上下文数据正是传入 Column 组件的 handle 实例的地方。
在这里插入图片描述
正好,之前在 ArkUIBaseNode 类中封装好了一个 GetHandle 方法,因此,可以通过在 ArkUIColumnNode 类中同样定义一个 GetHandle 方法去调用 ArkUIBaseNode 类的 GetHandle 并将 handle 也就组件的上下文给返回出去,从而解决在 testGestureExample 方法中无法直接访问 ArkUIBaseNode 类的 GetHandle 方法的问题。

在长按手势的回调处理函数 onActionCallBackPanLongPress 中,最开头的一段代码,同样是为了进行 UI 回显,这也是为了将之前已经完成的功能代码充分利用起来。

长按手势通过 setGestureEventTarget 接口绑定到 Column 组件身上后,别忘了用 gestureApi->addChildGesture(groupGesture, longPressGesture) 将长按手势给注册到手势组中。

4.4、创建快滑手势及其回调函数

// 创建快滑手势 swipe
auto swipeGesture = gestureApi->createSwipeGesture(1, GESTURE_DIRECTION_ALL, 100);
if (gestureApi->getGestureType) {ArkUI_GestureRecognizerType type = gestureApi->getGestureType(swipeGesture);OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager","GestureSampleLog, ArkUI_GestureRecognizerType %{public}d", type);
}
// 给快滑手势绑定回调
auto onActionCallBack = [](ArkUI_GestureEvent *event, void *extraParam) {delete globalParams->value;globalParams->value = new std::string("触发快滑事件");globalParams->desc = "触摸column";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);ArkUI_GestureEventActionType actionType = OH_ArkUI_GestureEvent_GetActionType(event);float velocity = OH_ArkUI_PanGesture_GetVelocity(event);float velocityX = OH_ArkUI_PanGesture_GetVelocityX(event);float velocityY = OH_ArkUI_PanGesture_GetVelocityY(event);float offsetX = OH_ArkUI_PanGesture_GetOffsetX(event);float offsetY = OH_ArkUI_PanGesture_GetOffsetY(event);float scale = OH_ArkUI_PinchGesture_GetScale(event);float centerX = OH_ArkUI_PinchGesture_GetCenterX(event);float centerY = OH_ArkUI_PinchGesture_GetCenterY(event);float angle = OH_ArkUI_SwipeGesture_GetAngle(event);float VelocityS = OH_ArkUI_SwipeGesture_GetVelocity(event);float angleR = OH_ArkUI_RotationGesture_GetAngle(event);float repeat = OH_ArkUI_LongPress_GetRepeatCount(event);// 通过日志查看OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager","GestureSampleLog, swipeGesture callback actionType: %{public}d, velocity ""%{public}f,velocityX ""%{public}f; ""velocityY %{public}f, offsetX %{public}f, offsetY %{public}f, scale %{public}fcenterX ""%{public}f centerY"" %{public}f angle %{public}f VelocityS %{public}f angleR %{public}f repeat %{public}f",actionType, velocity, velocityX, velocityY, offsetX, offsetY, scale, centerX, centerY, angle,VelocityS, angleR, repeat);ArkUI_NumberValue value[] = {{.f32 = 0}, {.f32 = 0}, {.f32 = 0}, {.f32 = angleR}, {.f32 = 0}};ArkUI_AttributeItem item = {value, 5};auto column = reinterpret_cast<ArkUI_NodeHandle>(extraParam);auto nodeAPI = NativeModuleInstance::GetInstance()->GetNativeNodeAPI();nodeAPI->setAttribute(column, NODE_ROTATE, &item);
};gestureApi->setGestureEventTarget(swipeGesture, GESTURE_EVENT_ACTION_ACCEPT | GESTURE_EVENT_ACTION_UPDATE | GESTURE_EVENT_ACTION_END, nodeHandle,onActionCallBack);// 将快滑手势添加到手势组
if (gestureApi->addChildGesture) {gestureApi->addChildGesture(groupGesture, swipeGesture);OH_LOG_Print(LOG_APP, LOG_INFO, 0xFF00, "Manager","GestureSampleLog, addChildGesture swipeGesture");
}

由于快滑手势的代码,整体上与长按手势的相同,所以,这里不再进行过多的说明。

4.5、绑定手势组

单一手势需要绑定到 colum 组件上,手势组同样也需要,因此,在返回 colum 之前,别忘了这一行重要的代码:

// 将手势组设置到组件上
gestureApi->addGestureToNode(nodeHandle, groupGesture, PRIORITY, NORMAL_GESTURE_MASK);

4.6、手势触发说明

在这里插入图片描述
长按手势的触发,自然不需要过多说明,但是快滑手势的触发,屏幕前的你就不一定知道。快滑手势,需要先在屏幕上按下一会然后快速滑动,如果没有按下一会就直接滑动,则不会触发快滑手势。

从上面的日志记录来看,长按手势和快滑手势都成功捕获,并步入了对应的回调函数,所以,对应的UI回显代码也被顺利执行。

http://www.dtcms.com/a/556991.html

相关文章:

  • OkHttp不同类型的HTTP请求的示例
  • 【Java Web学习 | 第四篇】CSS(3) -背景
  • PySide6集成yolo v8实现图片人物检测、视频人物检测以及摄像头人物检测
  • 求解器的智能决策之道
  • 卡片式网站p2p网站建设公司哪家好
  • Spring AI实现一个智能客服
  • 【浅析赛题,一等奖水平】思路模型数据相关资料!2025 年“大湾区杯”粤港澳金融数学建模竞赛B 题 稳定币的综合评价与发展分析~
  • 【攻防实战】通达OA文件上传联动Cobalt Strike打穿三层内网(上)
  • Linux应用开发-7-串口通讯与终端设备
  • 河北廊坊做网站一个网站后台怎么做
  • 企业培训考试系统源码php答题考试、题库、错题、练习考试等功能
  • 开拓视野:漫谈WebView领域相关技术
  • 如何在机器学习中使用特征提取对表格数据进行处理
  • UMI企业智脑助力数字化转型与智能化升级
  • xshell使用scp命令上传和下载文件
  • 命令行传参及调试——vscode平台
  • 【面试进阶】JavaScript 函数与对象进阶知识总结(重难点+记忆模板)
  • 《赋能AI解锁Coze智能体搭建核心技能(2)--- 智能体开发基础》
  • 自贡网站优化js网站开发视频教程
  • 驱动增长,而非浪费:8步整合SEO与PMax,解锁Google AI的隐藏流量
  • 【图像处理基石】如何在图像中实现光晕的星芒效果?
  • Node.js 解释环境变量的定义、作用及在Node.js中的重要性,区分开发、测试、生产环境配置需求。
  • Rust 快速入门:从零到上手的系统指南
  • 做家政网站网站公司做的网站有最字
  • kafka 延迟消费配置
  • Win32 API 简洁版
  • RocketMQ 是什么?它的架构是怎么样的?和 Kafka 又有什么区别?
  • 企业微信网站建设方案模板下载wordpress几种系统
  • Token Activation Map to Visually Explain Multimodal LLMs
  • RHCSA-15网络管理