HarmonyOS7 手势识别:捏合、长按、拖拽,交互全靠它
2026/7/24 9:25:38 网站建设 项目流程

文章目录

    • 前言
    • 手势体系概览
    • 点击与长按
      • TapGesture——双击点赞
      • LongPressGesture——长按弹出操作菜单
    • 拖拽——PanGesture 实战图片拖拽
    • 捏合缩放——PinchGesture 实战图片缩放
    • 手势组合——并行与串行
      • 并行组合——GestureGroup Parallel
      • 串行组合——GestureMode.Sequence
    • 写在最后

前言

你刷短视频双击点赞、看图捏合放大、长按弹出菜单——这些交互背后全是手势识别。HarmonyOS7 的手势系统比我想象的要完善,单手势、组合手势、优先级控制,该有的都有。但刚上手时确实容易晕:TapGesture 和 onClick 啥区别?PanGesture 方向怎么设?手势冲突怎么办?这篇把这些问题一次讲透。

手势体系概览

HarmonyOS7 提供的手势类型,一张表看全:

手势类型核心回调典型场景
TapGesture点击onAction双击点赞、多次点击
LongPressGesture长按onAction/onActionEnd长按菜单、拖拽预备
PanGesture拖拽/平移onActionStart/Update/End图片拖拽、滑动删除
PinchGesture捏合onActionStart/Update/End图片缩放
RotationGesture旋转onActionStart/Update/End图片旋转
SwipeGesture快速滑动onAction翻页、切歌

绑定方式有三种:

  • gesture():普通绑定,子组件优先
  • priorityGesture():优先绑定,父组件抢优先
  • parallelGesture():并行绑定,父子同时响应

划重点:大部分情况用gesture()就够了。只有父子组件手势冲突时才需要priorityGestureparallelGesture

点击与长按

TapGesture——双击点赞

你可能问了:不是有onClick吗?区别在于onClick只能响应单击,TapGesture能识别多次点击

Image($r('app.media.heart')).width(40).height(40).gesture(TapGesture({count:2}).onAction(()=>{this.toggleLike()}))

count: 2表示双击触发。还有个细节——绑定TapGesture({ count: 2 })后,单击不会触发任何回调。如果你需要单击和双击共存,得用组合手势(后面讲)。

LongPressGesture——长按弹出操作菜单

@StateisLongPressed:boolean=falseText('长按我试试').fontSize(20).gesture(LongPressGesture({repeat:true}).onAction((event:GestureEvent)=>{if(event.repeat){// 持续长按中,可以震动反馈}else{this.isLongPressed=true}}).onActionEnd(()=>{this.isLongPressed=false}))

参数repeat: true很关键——设了之后,长按过程中会持续触发onActionevent.repeattrue表示是重复触发。适合做"长按加速"效果,比如长按数字递增。

不设repeat的话,只在长按达到阈值时触发一次,手指抬起触发onActionEnd

拖拽——PanGesture 实战图片拖拽

这是最实用的手势之一,图片拖拽、列表滑动删除都用它:

@StateimageX:number=100@StateimageY:number=100Image($r('app.media.photo')).width(80).height(80).position({x:this.imageX,y:this.imageY}).gesture(PanGesture({direction:PanDirection.All}).onActionStart(()=>{console.info('开始拖拽')}).onActionUpdate((event:GestureEvent)=>{this.imageX+=event.offsetXthis.imageY+=event.offsetY}).onActionEnd(()=>{console.info('拖拽结束')}))

关键讲解:

  • direction: PanDirection.All——允许所有方向拖拽。也可以设PanDirection.HorizontalPanDirection.Vertical限制单方向
  • event.offsetX / offsetY——相对上一次回调的偏移量,不是绝对坐标。所以要用+=累加
  • 如果要限制拖拽范围,在onActionUpdate里加边界判断就行

捏合缩放——PinchGesture 实战图片缩放

看大图时捏合缩放,几乎成了用户本能:

@StateimageScale:number=1@StatelastScale:number=1Image($r('app.media.photo')).width(300).height(200).scale({x:this.imageScale,y:this.imageScale}).gesture(PinchGesture().onActionStart(()=>{this.lastScale=this.imageScale}).onActionUpdate((event:GestureEvent)=>{this.imageScale=this.lastScale*event.scale// 限制缩放范围if(this.imageScale<0.5)this.imageScale=0.5if(this.imageScale>3)this.imageScale=3}).onActionEnd(()=>{this.lastScale=this.imageScale}))

为什么需要lastScale

event.scale相对手势开始时的缩放比例,不是相对上一次的。比如当前 scale 是 2,用户捏合让event.scale变成 1.5,那实际应该是2 * 1.5 = 3,不是直接用 1.5。所以每次手势开始时记录当前 scale,update 时用lastScale * event.scale算真实值。

血泪教训:一定要限制缩放范围,不然用户一使劲缩到 0,图片就"没了";放大到 10 倍,直接卡死。

手势组合——并行与串行

单手势好理解,但实际需求经常要组合。比如"双击缩放 + 捏合缩放"、“长按后拖拽”。

并行组合——GestureGroup Parallel

双击缩放 + 捏合缩放同时生效:

.gesture(GestureGroup(GestureMode.Parallel,TapGesture({count:2}).onAction(()=>{animateTo({duration:200},()=>{this.imageScale=this.imageScale>1?1:2})}),PinchGesture().onActionUpdate((event:GestureEvent)=>{this.imageScale=this.lastScale*event.scale})))

GestureMode.Parallel表示两个手势互不干扰,各响各的。双击触发缩放切换,捏合触发连续缩放,两不误。

串行组合——GestureMode.Sequence

长按后拖拽——必须先长按触发,再拖拽才有效:

.gesture(GestureGroup(GestureMode.Sequence,LongPressGesture({duration:500}).onAction(()=>{// 长按触发,给出视觉反馈this.itemSelected=true}),PanGesture().onActionUpdate((event:GestureEvent)=>{this.itemX+=event.offsetXthis.itemY+=event.offsetY})))

串行模式下,手势按顺序识别。长按成功后才识别拖拽,任何一个失败则整个组合失败。这就像 Android 的长按拖拽——得先按住不动,才能拖。

还有个GestureMode.Exclusive(互斥),多个手势谁先识别算谁的,另一个取消。日常用得不多,知道就行。

写在最后

手势识别是交互体验的基础。搞懂了 Tap、LongPress、Pan、Pinch 四个手势,90% 的交互场景都能覆盖。组合手势稍微复杂点,但理解了并行和串行的区别就不难了。

实战建议:先从单手势开始做,调通了再组合。别上来就搞三四个手势并行,出了冲突调试到崩溃。手势和动画搭配使用效果翻倍——拖拽加个animateTo弹回,捏合加个平滑过渡,体验直接上一个档次。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询