HarmonyOS 5.1 手势事件详解
大家好,我是 V 哥。手势事件由绑定手势方法和绑定的手势组成,绑定的手势可以分为单一手势和组合手势两种类型,根据手势的复杂程度进行区分。本文跟着 V 哥一起来探讨手势事件处理。
想要考取鸿蒙认证的小伙伴,请加入 V 哥班级获取辅导:
V 哥写的鸿蒙全系例图书,助你从入门到一名成熟的开发者:

一、绑定手势方法
通过给各个组件绑定不同的手势事件,并设计事件的响应方式,当手势识别成功时,ArkUI 框架将通过事件回调通知组件手势识别的结果。
gesture(常规手势绑定方法),gesture 为通用的一种手势绑定方法,可以将手势绑定到对应的组件上,方法格式如下所示:
priorityGesture(带优先级的手势绑定方法),priorityGesture 是带优先级的手势绑定方法,可以在组件上绑定优先识别的手势,方法格式如下所示:
在默认情况下,当父组件和子组件使用 gesture 绑定同类型的手势时,子组件优先识别通过 gesture 绑定的手势。当父组件使用 priorityGesture 绑定与子组件同类型的手势时,父组件优先识别通过 priorityGesture 绑定的手势。长按手势时,设置触发长按的最短时间小的组件会优先响应,会忽略 priorityGesture 设置。比如当父组件 Column 和子组件 Text 同时绑定 TapGesture 手势时,父组件以带优先级手势 priorityGesture 的形式进行绑定时,优先响应父组件绑定的 TapGesture。示例代码如下所示:
Demo0701.ets 示例
效果如下图所示:

parallelGesture(并行手势绑定方法),是并行的手势绑定方法,可以在父子组件上绑定可以同时响应的相同手势。
在默认情况下,手势事件为非冒泡事件,当父子组件绑定相同的手势时,父子组件绑定的手势事件会发生竞争,最多只有一个组件的手势事件能够获得响应。而当父组件绑定了并行手势 parallelGesture 时,父子组件相同的手势事件都可以触发,实现类似冒泡效果。
二、单一手势
点击手势(TapGesture),点击手势支持单次点击和多次点击,拥有两个可选参数:count:声明该点击手势识别的连续点击次数。默认值为 1,若设置小于 1 的非法值会被转化为默认值。如果配置多次点击,上一次抬起和下一次按下的超时时间为 300 毫秒。fingers:用于声明触发点击的手指数量,最小值为 1,最大值为 10,默认值为 1。当配置多指时,若第一根手指按下 300 毫秒内未有足够的手指数按下则手势识别失败。方法格式如下所示:
长按手势(LongPressGesture),长按手势用于触发长按手势事件,拥有三个可选参数:fingers:用于声明触发长按手势所需要的最少手指数量,最小值为 1,最大值为 10,默认值为 1。repeat:用于声明是否连续触发事件回调,默认值为 false。duration:用于声明触发长按所需的最短时间,单位为毫秒,默认值为 500。方法格式如下所示:
以在 Text 组件上绑定可以重复触发的长按手势为例,演示长按手势,代码如下所示:
Demo0702.ets 示例
拖动手势(PanGesture),拖动手势用于触发拖动手势事件,滑动达到最小滑动距离(默认值为 5vp)时拖动手势识别成功,拥有三个可选参数:fingers:用于声明触发拖动手势所需要的最少手指数量,最小值为 1,最大值为 10,默认值为 1。direction:用于声明触发拖动的手势方向,此枚举值支持逻辑与(&)和逻辑或(|)运算。默认值为 Pandirection.All。distance:用于声明触发拖动的最小拖动识别距离,单位为 vp,默认值为 5。方法格式如下所示:
以在 Text 组件上绑定拖动手势为例,可以通过在拖动手势的回调函数中修改组件的布局位置信息来实现组件的拖动,实现代码如下所示:
Demo0703.ets 示例
效果如下所示:

大部分可滑动组件,如 List、Grid、Scroll、Tab 等组件是通过 PanGesture 实现滑动,在组件内部的子组件绑定拖动手势(PanGesture)或者滑动手势(SwipeGesture)会导致手势竞争。
当在子组件绑定 PanGesture 时,在子组件区域进行滑动仅触发子组件的 PanGesture。如果需要父组件响应,需要通过修改手势绑定方法或者子组件向父组件传递消息进行实现,或者通过修改父子组件的 PanGesture 参数 distance 使得拖动更灵敏。当子组件绑定 SwipeGesture 时,由于 PanGesture 和 SwipeGesture 触发条件不同,需要修改 PanGesture 和 SwipeGesture 的参数以达到所需效果。不合理的阈值设置会导致滑动不跟手(响应时延慢)的问题。4.捏合手势(PinchGesture),捏合手势用于触发捏合手势事件,拥有两个可选参数:fingers:用于声明触发捏合手势所需要的最少手指数量,最小值为 2,最大值为 5,默认值为 2。distance:用于声明触发捏合手势的最小距离,单位为 vp,默认值为 5,方法格式如下所示:
我们可以在 Column 组件上绑定三指捏合手势,可以通过在捏合手势的函数回调中获取缩放比例,实现对组件的缩小或放大,代码如下所示:
Demo0704.ets 示例
旋转手势(RotationGesture),旋转手势用于触发旋转手势事件,拥有两个可选参数:fingers:用于声明触发旋转手势所需要的最少手指数量,最小值为 2,最大值为 5,默认值为 2。angle:用于声明触发旋转手势的最小改变度数,单位为 deg,默认值为 1。方法格式如下所示:
我们可以在 Text 组件上绑定旋转手势实现组件的旋转为例,可以通过在旋转手势的回调函数中获取旋转角度,从而实现组件的旋转,代码示例如下图所示:
Demo0705.ets 示例
效果如下图所示:

滑动手势(SwipeGesture),滑动手势用于触发滑动事件,当滑动速度大于 100vp/s 时可以识别成功,拥有三个可选参数:fingers:用于声明触发滑动手势所需要的最少手指数量,最小值为 1,最大值为 10,默认值为 1。direction:用于声明触发滑动手势的方向,此枚举值支持逻辑与(&)和逻辑或(|)运算。默认值为 SwipeDirection.All。speed:用于声明触发滑动的最小滑动识别速度,单位为 vp/s,默认值为 100。方法格式如下所示:
我们可以在 Column 组件上绑定滑动手势实现组件的旋转为例,代码如下所示:
Demo0706.ets 示例
效果如下所示:

当 SwipeGesture 和 PanGesture 同时绑定时,若二者是以默认方式或者互斥方式进行绑定时,会发生竞争。SwipeGesture 的触发条件为滑动速度达到 100vp/s,PanGesture 的触发条件为滑动距离达到 5vp,先达到触发条件的手势触发。可以通过修改 SwipeGesture 和 PanGesture 的参数以达到不同的效果。
三、组合手势
组合手势由多种单一手势组合而成,通过在 GestureGroup 中使用不同的 GestureMode 来声明该组合手势的类型。方法主要包括 2 个参数分别为:mode:为 GestureMode 枚举类。用于声明该组合手势的类型,取值顺序识别、并行识别和互斥识别三种类型。gesture:由多个手势组合而成的数组。用于声明组合成该组合手势的各个手势。方法具体格式如下所示:
顺序识别
顺序识别组合手势对应的 GestureMode 为 Sequence。顺序识别组合手势将按照手势的注册顺序识别手势,直到所有的手势识别成功。当顺序识别组合手势中有一个手势识别失败时,后续手势识别均失败。顺序识别手势组仅有最后一个手势可以响应 onActionEnd。以一个由长按手势和拖动手势组合而成的连续手势为例,在一个 Column 组件上绑定了 translate 属性,通过修改该属性可以设置组件的位置移动。然后在该组件上绑定 LongPressGesture 和 PanGesture 组合而成的 Sequence 组合手势。当触发 LongPressGesture 时,更新显示的数字。当长按后进行拖动时,根据拖动手势的回调函数,实现组件的拖动。实现代码示例如下所示:
Demo0707.ets 示例
效果如下图所示:

拖拽事件是一种典型的顺序识别组合手势事件,由长按手势事件和滑动手势事件组合而成。只有先长按达到长按手势事件预设置的时间后进行滑动才会触发拖拽事件。如果长按事件未达到或者长按后未进行滑动,拖拽事件均识别失败。
并行识别并行识别组合手势对应的 GestureMode 为 Parallel。并行识别组合手势中注册的手势将同时进行识别,直到所有手势识别结束。并行识别手势组合中的手势进行识别时互不影响。以在一个 Column 组件上绑定点击手势和双击手势组成的并行识别手势为例,由于单击手势和双击手势是并行识别,因此两个手势可以同时进行识别,二者互不干涉。实现代码如下所示:
Demo0708.ets 示例
效果如下图所示:


当由单击手势和双击手势组成一个并行识别组合手势后,在区域内进行点击时,单击手势和双击手势将同时进行识别。当只有单次点击时,单击手势识别成功,双击手势识别失败。
当有两次点击时,若两次点击相距时间在规定时间内(默认规定时间为 300 毫秒),触发两次单击事件和一次双击事件。当有两次点击时,若两次点击相距时间超出规定时间,触发两次单击事件不触发双击事件。
互斥识别
互斥识别组合手势对应的 GestureMode 为 Exclusive。互斥识别组合手势中注册的手势将同时进行识别,若有一个手势识别成功,则结束手势识别,其他所有手势识别失败。
以在一个 Column 组件上绑定单击手势和双击手势组合而成的互斥识别组合手势为例。若先绑定单击手势后绑定双击手势,由于单击手势只需要一次点击即可触发而双击手势需要两次,每次的点击事件均被单击手势消费而不能积累成双击手势,所以双击手势无法触发。若先绑定双击手势后绑定单击手势,则触发双击手势不触发单击手势。代码实现如下所示:
Demo0709.ets 示例
效果如下图所示:


当由单击手势和双击手势组成一个互斥识别组合手势后,在区域内进行点击时,单击手势和双击手势将同时进行识别。当只有单次点击时,单击手势识别成功,双击手势识别失败。
当有两次点击时,手势响应取决于绑定手势的顺序。若先绑定单击手势后绑定双击手势,单击手势在第一次点击时即宣告识别成功,此时双击手势已经失败。即使在规定时间内进行了第二次点击,双击手势事件也不会进行响应,此时会触发单击手势事件的第二次识别成功。若先绑定双击手势后绑定单击手势,则会响应双击手势不响应单击手势。
四、多层级手势事件
多层级手势事件指父子组件嵌套时,父子组件均绑定了手势或事件。在该场景下,手势或者事件的响应受到多个因素的影响,相互之间发生传递和竞争,容易出现预期外的响应。
触摸事件
触摸事件(onTouch 事件)是所有手势组成的基础,有 Down,Move,Up,Cancel 四种。手势均由触摸事件组成,例如,点击为 Down+Up,滑动为 Down+一系列 Move+Up。触摸事件具有最特殊性:
监听了 onTouch 事件的组件。若在手指落下时被触摸则均会收到 onTouch 事件的回调,被触摸受到触摸热区和触摸控制影响。
onTouch 事件的回调是闭环的。若一个组件收到了手指 Id 为 0 的 Down 事件,后续也会收到手指 Id 为 0 的 Move 事件和 Up 事件。
onTouch 事件的回调是一致的。若一个组件收到了手指 Id 为 0 的 Down 事件未收到手指 Id 为 1 的 Down 事件,则后续只会收到手指 Id 为 0 的 touch 事件,不会收到手指 Id 为 1 的后续 touch 事件。
对于一般的容器组件(例如:Column),父子组件之间 onTouch 事件能够同时触发,兄弟组件之间 onTouch 事件根据布局进行触发。
组件 B 和组件 C 作为组件 A 的子组件,当触摸到组件 B 或者组件 C 时,组件 A 也会被触摸到。onTouch 事件允许多个组件同时触发。因此,当触摸组件 B 时,会触发组件 A 和组件 B 的 onTouch 回调,不会触发组件 C 的 onTouch 回调。
当触摸组件 C 时,会触发组件 A 和组件 C 的 onTouch 回调,不触发组件 B 的回调。特殊的容器组件,如 Stack 等组件,由于子组件之间存在着堆叠关系,子组件的布局也互相存在遮盖关系。所以,父子组件之间 onTouch 事件能够同时触发,兄弟组件之间 onTouch 事件会存在遮盖关系。
手势与事件
除了触摸事件(onTouch 事件)外的所有手势与事件,均是通过基础手势或者组合手势实现的。比如拖拽事件是由长按手势和滑动手势组成的一个顺序手势。在未显式声明的情况下,同一时间,一根手指对应的手势组中只会有一个手势获得成功从而触发所设置的回调。因此,除非显式声明允许多个手势同时成功,同一时间只会有一个手势响应。响应优先级遵循以下条件:
当父子组件均绑定同一类手势时,子组件优先于父组件触发。
当一个组件绑定多个手势时,先达到手势触发条件的手势优先触发。
当父组件和子组件均绑定点击手势时,子组件的优先级高于父组件。因此,当在 B 组件上进行点击时,组件 B 所绑定的 TapGesture 的回调会被触发,而组件 A 所绑定的 TapGesture 的回调不会被触发。
当组件 A 上绑定了由点击和滑动手势组成的互斥手势组时,先达到手势触发条件的手势触发对应的回调。若使用者做了一次点击操作,则响应点击对应的回调。若使用者进行了一次滑动操作并且滑动距离达到了阈值,则响应滑动对应的回调。
可以通过设置属性,控制默认的多层级手势事件竞争流程,更好的实现手势事件。目前,responseRegion 属性和 hitTestBehavior 属性可以控制 Touch 事件的分发,从而可以影响到 onTouch 事件和手势的响应。而绑定手势方法属性可以控制手势的竞争从而影响手势的响应,但不能影响到 onTouch 事件。
responseRegion 对手势和事件的控制
responseRegion 属性可以实现组件的响应区域范围的变化。响应区域范围可以超出或者小于组件的布局范围。
当组件 A 绑定了.responseRegion({Rect4})的属性后,所有落在 Rect4 区域范围的触摸事件和手势可被组件 A 对应的回调响应。
当组件 B 绑定了.responseRegion({Rect1, Rect2, Rect3})的属性后,所有落在 Rect1,Rect2 和 Rect3 区域范围的触摸事件和手势可被组件 B 对应的回调响应。
当绑定了 responseRegion 后,手势与事件的响应区域范围将以所绑定的区域范围为准,而不是以布局区域为准,可能出现布局相关区域不响应手势与事件的情况。此外,responseRegion 属性支持由多个 Rect 组成的数组作为入参,以支持更多开发需求。
hitTestBehavior 对手势和事件的控制
hitTestBehavior 属性可以实现在复杂的多层级场景下,一些组件能够响应手势和事件,而一些组件不能响应手势和事件。
HitTestMode.Block 自身会响应触摸测试,阻塞子节点和兄弟节点的触摸测试,从而导致子节点和兄弟节点的 onTouch 事件和手势均无法触发。
当组件 C 未设置 hitTestBehavior 时,点击组件 D 区域,组件 A、组件 C 和组件 D 的 onTouch 事件会触发,组件 D 的点击手势会触发。
当组件 C 设置了 hitTestBehavior 为 HitTestMode.Block 时,点击组件 D 区域,组件 A 和组件 C 的 onTouch 事件会触发,组件 D 的 onTouch 事件未触发。同时,由于组件 D 的点击手势因为被阻塞而无法触发,组件 C 的点击手势会触发。
HitTestMode.Transparent 自身响应触摸测试,不会阻塞兄弟节点的触摸测试。当组件 C 未设置 hitTestBehavior 时,点击组件 B 和组件 C 的重叠区域时,Stack A 和组件 C 的 onTouch 事件会触发,组件 C 的点击事件会触发,组件 B 的 onTouch 事件和点击手势均不触发。
而当组件 C 设置 hitTestBehavior 为 HitTestMode.Transparent 时,点击组件 B 和组件 C 的重叠区域,组件 A 和组件 C 不受到影响与之前一致,组件 A 和组件 C 的 onTouch 事件会触发,组件 C 的点击手势会触发。而组件 B 因为组件 C 设置了 HitTestMode.Transparent,组件 B 也收到了 Touch 事件,从而组件 B 的 onTouch 事件和点击手势触发。
HitTestMode.None 自身不响应触摸测试,不会阻塞子节点和兄弟节点的触摸控制。当组件 A 未设置 hitTestBehavior 时,点击组件 B 区域时,组件 A 和组件 B 的 onTouch 事件均会触发,组件 B 的点击手势会触发。
当组件 A 设置 hitTestBehavior 为 HitTestMode.None 时,点击组件 B 区域时,组件 B 的 onTouch 事件触发,而组件 A 的 onTouch 事件无法触发,组件 B 的点击手势触发。针对简单的场景,建议在单个组件上绑定 hitTestBehavior。针对复杂场景,建议在多个组件上绑定不同的 hitTestBehavior 来控制 Touch 事件的分发。
绑定手势方法对手势的控制
设置绑定手势的方法可以实现在多层级场景下,当父组件与子组件绑定了相同的手势时,设置不同的绑定手势方法有不同的响应优先级。当父组件使用.gesture 绑定手势,父子组件所绑定手势类型相同时,子组件优先于父组件响应。
当父子组件均正常绑定点击手势时,子组件优先于父组件响应。此时,单击组件 B 区域范围,组件 B 的点击手势会触发,组件 A 的点击手势不会触发。如果以带优先级的方式绑定手势,则可使得父组件所绑定手势的响应优先级高于子组件。
当父组件以.priorityGesture 的形式绑定手势时,父组件所绑定的手势优先级高于子组件。此时,单击组件 B 区域范围,组件 A 的点击手势会触发,组件 B 的点击手势不会触发。如果需要父子组件所绑定的手势不发生冲突,均可响应,则可以使用并行的方式在父组件绑定手势。
当父组件以.parallelGesture 的形式绑定手势时,父组件和子组件所绑定的手势均可触发。此时,单击组件 B 区域范围,组件 A 和组件 B 的点击手势均会触发。
小结
本文详细介绍了 Harmony OS 中的交互事件处理机制,涵盖了触屏事件、键鼠事件、焦点事件和拖拽事件等多种类型。首先,对 Harmony OS 中的事件分类进行了详细阐述,包括触屏事件、键鼠事件、焦点事件和拖拽事件,并解释了事件分发机制和手势事件的构成。接着,深入探讨了事件分发的具体过程,包括触摸测试和事件响应链的收集,以及如何通过触摸测试控制和自定义事件拦截来优化事件处理。
在触屏事件部分,详细讲解了点击事件、触摸事件等的触发条件和回调函数的使用方法,并通过示例代码展示了如何在实际应用中实现这些事件的处理。键鼠事件部分则介绍了鼠标事件和键盘事件的分类及其触发机制,包括鼠标悬浮、点击等事件的处理方法,以及如何通过键盘事件实现快捷键功能。
焦点事件的介绍包括焦点的基本概念、焦点链的形成和走焦规范,以及如何通过监听获焦和失焦事件来实现焦点的动态管理。此外,还介绍了如何设置组件的可获焦属性和焦点样式,以及如何使用 FocusController 实现主动获焦和失焦。
拖拽事件部分则详细介绍了拖拽操作的基本概念、触发方式和回调事件的使用,包括手势拖拽和鼠标拖拽的不同实现方式,以及如何通过设置拖拽背板图和使用 UDMF 实现数据的传递。
最后,手势事件的讲解涵盖了单一手势和组合手势的分类及其绑定方法,包括点击手势、长按手势、拖动手势、捏合手势、旋转手势和滑动手势等的使用场景和实现方式,并通过示例代码展示了如何在实际应用中实现复杂的手势交互。
通过本文的学习,读者可以全面掌握 Harmony OS 中各种交互事件的处理方法,提升应用的交互体验和用户满意度。
版权声明: 本文为 InfoQ 作者【威哥爱编程】的原创文章。
原文链接:【http://xie.infoq.cn/article/a07ee38fa778bbc9c8351ce84】。
本文遵守【CC-BY 4.0】协议,转载请保留原文出处及本版权声明。
评论