HarmonyOS7 手势识别:捏合、长按、拖拽,交互全靠它

HarmonyOS7 手势识别:捏合、长按、拖拽,交互全靠它 文章目录前言手势体系概览点击与长按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()就够了。只有父子组件手势冲突时才需要priorityGesture和parallelGesture。点击与长按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:booleanfalseText(长按我试试).fontSize(20).gesture(LongPressGesture({repeat:true}).onAction((event:GestureEvent){if(event.repeat){// 持续长按中可以震动反馈}else{this.isLongPressedtrue}}).onActionEnd((){this.isLongPressedfalse}))参数repeat: true很关键——设了之后长按过程中会持续触发onActionevent.repeat为true表示是重复触发。适合做长按加速效果比如长按数字递增。不设repeat的话只在长按达到阈值时触发一次手指抬起触发onActionEnd。拖拽——PanGesture 实战图片拖拽这是最实用的手势之一图片拖拽、列表滑动删除都用它StateimageX:number100StateimageY:number100Image($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.imageXevent.offsetXthis.imageYevent.offsetY}).onActionEnd((){console.info(拖拽结束)}))关键讲解direction: PanDirection.All——允许所有方向拖拽。也可以设PanDirection.Horizontal或PanDirection.Vertical限制单方向event.offsetX / offsetY——相对上一次回调的偏移量不是绝对坐标。所以要用累加如果要限制拖拽范围在onActionUpdate里加边界判断就行捏合缩放——PinchGesture 实战图片缩放看大图时捏合缩放几乎成了用户本能StateimageScale:number1StatelastScale:number1Image($r(app.media.photo)).width(300).height(200).scale({x:this.imageScale,y:this.imageScale}).gesture(PinchGesture().onActionStart((){this.lastScalethis.imageScale}).onActionUpdate((event:GestureEvent){this.imageScalethis.lastScale*event.scale// 限制缩放范围if(this.imageScale0.5)this.imageScale0.5if(this.imageScale3)this.imageScale3}).onActionEnd((){this.lastScalethis.imageScale}))为什么需要lastScaleevent.scale是相对手势开始时的缩放比例不是相对上一次的。比如当前 scale 是 2用户捏合让event.scale变成 1.5那实际应该是2 * 1.5 3不是直接用 1.5。所以每次手势开始时记录当前 scaleupdate 时用lastScale * event.scale算真实值。血泪教训一定要限制缩放范围不然用户一使劲缩到 0图片就没了放大到 10 倍直接卡死。手势组合——并行与串行单手势好理解但实际需求经常要组合。比如双击缩放 捏合缩放、“长按后拖拽”。并行组合——GestureGroup Parallel双击缩放 捏合缩放同时生效.gesture(GestureGroup(GestureMode.Parallel,TapGesture({count:2}).onAction((){animateTo({duration:200},(){this.imageScalethis.imageScale1?1:2})}),PinchGesture().onActionUpdate((event:GestureEvent){this.imageScalethis.lastScale*event.scale})))GestureMode.Parallel表示两个手势互不干扰各响各的。双击触发缩放切换捏合触发连续缩放两不误。串行组合——GestureMode.Sequence长按后拖拽——必须先长按触发再拖拽才有效.gesture(GestureGroup(GestureMode.Sequence,LongPressGesture({duration:500}).onAction((){// 长按触发给出视觉反馈this.itemSelectedtrue}),PanGesture().onActionUpdate((event:GestureEvent){this.itemXevent.offsetXthis.itemYevent.offsetY})))串行模式下手势按顺序识别。长按成功后才识别拖拽任何一个失败则整个组合失败。这就像 Android 的长按拖拽——得先按住不动才能拖。还有个GestureMode.Exclusive互斥多个手势谁先识别算谁的另一个取消。日常用得不多知道就行。写在最后手势识别是交互体验的基础。搞懂了 Tap、LongPress、Pan、Pinch 四个手势90% 的交互场景都能覆盖。组合手势稍微复杂点但理解了并行和串行的区别就不难了。实战建议先从单手势开始做调通了再组合。别上来就搞三四个手势并行出了冲突调试到崩溃。手势和动画搭配使用效果翻倍——拖拽加个animateTo弹回捏合加个平滑过渡体验直接上一个档次。