HarmonyOS-ArkUI 手势系列1--API分析及使用方式及示例
手势分类
手势是一种通过手部的动作来控制设备交互的方式, 是人机交互中重要的组成方式,例如我们对一个手机屏幕的点击, 双击, 捏合,这类动作,都是手势。
手势具体可以分为
- 单一手势, 指的是由一个简单独立的动作构成的手势,通常会涉及到一个明确的操作, 是手势事件中最为基础的形式,如单指单指多指的,单击,双击, 滑动,长按等等。
- 组合手势: 将多个单一的手势,按照特定的顺序(同时发生, 同时只有一个发生,必须按照顺序发生)组合起来,形成给为复杂的手势操作,可以完成就更加丰富的交互方式。例如微信朋友圈编辑动态,九宫格照片拖拽的逻辑,实现照片位置的对调。
- 多层级手势,常见于父子组件嵌套时,比较复杂的交互逻辑。比如地图应用的地图查看场景
单一手势
单一手势分类
|
手势名称 |
API |
动作 |
|
点击手势 |
TapGesture |
手指轻触屏幕 |
|
长按手势 |
LongPressGesture |
手指按住屏幕一段时间 |
|
滑动手势 |
SwiperGesture |
手指在屏幕上快速滑动 |
|
旋转手势 |
RotationGesture |
两根手指在屏幕上做旋转动作 |
|
捏合手势 |
PinchGesture |
两根手指在屏幕上向内靠拢或者向外分开 |
|
拖动手势 |
PanGesture |
单指按住屏幕上的某个元素并拖动手指 |
API整体分析
如果想监听上述的手势类型,需要在控件上加一个gesture属性。 我们以对双击手势的监听为例,代码如下:
Text('测试一下双点击手势')
.id(`tapGestureAccess`)
.fontSize(45)
.alignRules({
start: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top },
})
.gesture(TapGesture({ count: 2, fingers: 2 }) // 核心api。也就是调用gesture方法
.onAction((event) => {
if (event) {
this.resetAllData()
this.tapGestureText = `点击事件onAction, event=${JSON.stringify(event)}`
}
}), GestureMask.Normal)
所有的单一属性都是靠gesture来设置的。gesture方法声明于CommonMethod文件中,也就意味着,所有的控件都具备这个属性,都具备监听手势的能力。我们首先大致的了解下这个API的结构。

上图可知:
- gesture指定监听什么类型的手势,靠GestureType传入的是谁。而GestureType有7类,代表各类我们上述讲的手势。这些手势配置接口,全部继承自GestureInteface类。
- gesture拦截模式,靠GestureMask来指定。 Normal代表按照系统顺序,优先分发给可以消费该手势的子控件。和Android保持一致。 而另一个值IgnoreInternal则会屏蔽子组件手势,父组件和子组件都具备响应能力的场景,优先处理父组件。
各类单一手势如何使用API
以点击手势-TapGestureInterface为例分析
点击手势支持单击和双击, 单手指和多手指。配置是十分灵活的。具体API拆析如图所示:

上图可以看出,我们如果监听点击手势,可以调用UI组件的gesture方法, 并将第一个入参指定为TapGestureInterface, 第二个参数随您的需求自己定义。
而TapGestureInterface具备两个能力:
- 指定要监听的手势的细节, 例如TapGestureInterface的构造参数中,就有count(您想监听连续几次点击), finger(这个点击是几个手指动作), distance(手指最大的移动距离,多出不算点击, 在范围内才算点击)三个参数。
- 回调,即系统识别出您指定要求的手势后立马给与回调。
理解到这里的话, 我们基本不用研究文档了, 甚至可以写出比文档中还要细致的案例出来。因为所有接口参数心里都有数了。
接下来我们就对比着图示写一下代码,在写的过程中发现,报错了! 那么是怎么回事呢?

但是源码里明明这样声明的:

而关于TapGestureInterface的信息, 源码中还有一个细节:

为什么没有办法直接按照TapGestureInterface来进行参数构造呢?这里面涉及到ArkTs中的语法问题:
第一张图:在ArkTS中, interface不能直接初始化为对象, 也就是不能new。 这个和Java是一致的。所以第一张图报错正常。如果不想报错, 您也可以自己写一个TapGestureInterface的实现类。然后初始化这个实例作为参数传进去。
第二张图:GestureType是一种联合类型,它另一方面也列出了,手势动作所有的类型。
第三张图:TapGesture 是一个常量,既然为常量,则意味着在编译的时候就已经清清楚楚的掌握了大小,并约定了所占的空间。 TapGesture作为常量在声明的时候必须要初始化的, 所以这个TapGesture一定是一个有效的实例类, 这个类是实现了TapGestureInterface!这个比较有意思, 里面包含了一种不向外部公布,而又能实现具体功能,又能让外部使用的类,用户不用关心这个类具体实现方式,只管拿过来系统根据此类初始化的实例使用就可以了。

PS:这种设计方式,有个比较专业的名称,叫 接口类型化工厂常量 模式。好处就是为开发者提供简洁统一的调用入口,并隐藏框架的实现细节,并可以支撑运行时动态实例化。 就像您永远可以不知道TapGestureInterfaceImpl的存在那样。
好,那么这样的话,我们就可以清晰的知道代码应当怎么调用了, 即您在gesture方法中,第一个参数可以直接传入TapGesture常量,您可以通过修改这个常量,而获取对应的监听。
代码案例
Text('测试一下双点击手势')
.id(`tapGestureAccess`)
.fontSize(45)
.alignRules({
start: { anchor: '__container__', align: HorizontalAlign.Start },
top: { anchor: '__container__', align: VerticalAlign.Top },
})
.gesture(TapGesture({ count: 2, fingers: 2 }) //直接用系统给出的TapGesture常量,并说明是两个手指双击
.onAction((event) => {
if (event) {
this.resetAllData()
this.tapGestureText = `点击事件onAction, event=${JSON.stringify(event)}`
}
}), GestureMask.Normal)
}
关于回调用的GestureEvent里面都有什么内容
{
"repeat": false,
"offsetX": 0,
"offsetY": 0,
"scale": 1,
"angle": 0,
"speed": 0,
"timestamp": 43491339572000,
"pinchCenterX": 0,
"pinchCenterY": 0,
"source": 2,
"pressure": 2.8299999237060547,
"tiltX": 0,
"tiltY": 0,
"rollAngle": 0,
"sourceTool": 1,
"velocityX": 0,
"velocityY": 0,
"velocity": 0,
"fingerList": [
{
"id": 0,
"hand": 0,
"globalX": 238.22222222222223,
"globalY": 167.7037037037037,
"localX": 237.03703703703704,
"localY": 26.666666666666668,
"displayX": 238.22222222222223,
"displayY": 167.7037037037037
},
{
"id": 1,
"hand": 0,
"globalX": 151.40740740740742,
"globalY": 168.2962962962963,
"localX": 150.22222222222223,
"localY": 27.25925925925926,
"displayX": 151.40740740740742,
"displayY": 168.2962962962963
}
],
"deviceId": 5,
"target": {
"area": {
"position": {
"x": 1.1851851851851851,
"y": 97.25925925925925
},
"globalPosition": {
"x": 1.1851851851851851,
"y": 141.11111111111111
},
"width": 360.2962962962963,
"height": 105.48148148148148
},
"id": "tapGestureAccess"
},
"axisVertical": 0,
"axisHorizontal": 0,
"targetDisplayId": 0
}
可以看出和我们图中分析的API是保持一致的。在特殊的情况下, 或者是完全自定义的高难度组件中, 设计交互,会用到这些数据。对于单一手势交互,就不用在意里面的内容了。

更多推荐


所有评论(0)