HarmonyOS7 手势综合展示:六种手势类型一网打尽 + Grid 数据看板

文章目录

    • 前言
    • 效果展示
    • 方案设计
      • 多层手势共存策略
    • 详细实现
      • 状态变量
      • 日志函数
      • Grid 统计看板
      • statCard Builder
      • 三层手势绑定
      • 滑动方向判定
      • 日志面板
    • 踩坑记录
    • 写在最后

前言

前面十几篇文章分别讲了 ArkUI 的各种手势类型,今天来一个"全家桶"——把点击、长按、双击、拖拽、缩放、旋转、滑动全部集成到一个界面里,用 Grid 布局做数据看板,实时显示每种手势的触发次数和状态。这篇文章既是手势系列的总结,也是一个手势调试工具的设计参考。

效果展示

界面分三个区域:

  1. 统计看板:6 个 Grid 格子,分别显示点击/长按/双击/滑动/拖拽/缩放的计数或状态

  1. 测试区域:一个大色块,支持所有手势操作
  2. 日志面板:记录最近 8 条手势操作的时间线

方案设计

多层手势共存策略

一个组件上要同时支持 7 种手势,怎么组织?答案是分三层

层级手势模式
第一层双击、单击、长按Exclusive(互斥)
第二层拖拽、缩放、旋转Parallel(并行)

| 第三层 | 快速滑动 | 独立绑定 |

三层手势用三个.gesture()调用分别绑定。ArkUI 允许多次调用.gesture(),后面的不会覆盖前面的。

详细实现

状态变量

@Entry@Componentstruct GestureShowcase{@StatetapCount:number=0@StatelongPressCount:number=0@StateswipeDirection:string='-'@StatepanOffset:string='(0, 0)'@StatescaleValue:number=1@StateangleValue:number=0@StatedoubleTapCount:number=0@StategestureLog:string[]=[]// ...}

每种手势对应一个状态变量,gestureLog记录操作日志。

日志函数

logGesture(name:string){consttime=newDate().toLocaleTimeString()this.gestureLog.unshift(`[${time}]${name}`)if(this.gestureLog.length>8){this.gestureLog.pop()}}

每次手势触发时调用,新日志插入数组头部,超过 8 条就移除最旧的。unshift+pop组合实现了固定长度的 FIFO 队列。

Grid 统计看板

Grid(){GridItem(){this.statCard('点击',`${this.tapCount}`,'#4D96FF')}GridItem(){this.statCard('长按',`${this.longPressCount}`,'#FF6B6B')}GridItem(){this.statCard('双击',`${this.doubleTapCount}`,'#9B59B6')}GridItem(){this.statCard('滑动',this.swipeDirection,'#6BCB77')}GridItem(){this.statCard('拖拽',this.panOffset,'#FFA500')}GridItem(){this.statCard('缩放',`${Math.floor(this.scaleValue*100)}%`,'#4ECDC4')}}.columnsTemplate('1fr 1fr 1fr').rowsTemplate('1fr 1fr').rowsGap(8).columnsGap(8).width('100%').height(160).margin({bottom:16})

3 列 2 行的 Grid 布局,每个格子用@Builder函数statCard统一渲染。

statCard Builder

@BuilderstatCard(label:string,value:string,color:string){Column(){Text(value).fontSize(18).fontWeight(FontWeight.Bold).fontColor(color)Text(label).fontSize(10).fontColor('#999999').margin({top:2})}.width('100%').height('100%').backgroundColor('#F8F8F8').borderRadius(8).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center)}

@Builder是 ArkUI 的自定义构建函数,可以在组件的build()方法里调用。这里传入标签、数值和颜色,生成统一风格的统计卡片。

三层手势绑定

Column(){Text('手势测试区域').fontSize(16).fontColor('#FFFFFF')Text('点击/长按/双击/拖动/缩放/旋转').fontSize(11).fontColor('#FFFFFF').opacity(0.8)}.width('100%').height(140).backgroundColor('#667eea').borderRadius(16).justifyContent(FlexAlign.Center).alignItems(HorizontalAlign.Center).scale({x:this.scaleValue,y:this.scaleValue}).rotate({angle:this.angleValue})// 第一层:互斥手势.gesture(GestureGroup(GestureMode.Exclusive,TapGesture({count:2}).onAction(()=>{this.doubleTapCount++this.logGesture('双击')}),TapGesture({count:1}).onAction(()=>{this.tapCount++this.logGesture('单击')}),LongPressGesture({duration:500}).onAction(()=>{this.longPressCount++this.logGesture('长按')})))// 第二层:并动手势.gesture(GestureGroup(GestureMode.Parallel,PanGesture({direction:PanDirection.All}).onActionUpdate((event:GestureEvent)=>{this.panOffset=`(${Math.floor(event.offsetX)},${Math.floor(event.offsetY)})`}).onActionEnd(()=>{this.logGesture('拖拽')}),PinchGesture({fingers:2}).onActionUpdate((event:GestureEvent)=>{this.scaleValue=event.scale}).onActionEnd(()=>{this.logGesture('缩放')}),RotationGesture({fingers:2}).onActionUpdate((event:GestureEvent)=>{this.angleValue=event.angle}).onActionEnd(()=>{this.logGesture('旋转')})))// 第三层:滑动手势.gesture(SwipeGesture({direction:SwipeDirection.All}).onAction((event:GestureEvent)=>{constangle=event.angle??0if(angle>=45&&angle<135)this.swipeDirection='↑'elseif(angle>=135&&angle<225)this.swipeDirection='←'elseif(angle>=225&&angle<315)this.swipeDirection='↓'elsethis.swipeDirection='→'this.logGesture('滑动')}))

这段代码是手势系列知识的大综合。注意scalerotate直接绑在组件上,缩放和旋转操作能实时看到效果。

滑动方向判定

constangle=event.angle??0if(angle>=45&&angle<135)this.swipeDirection='↑'elseif(angle>=135&&angle<225)this.swipeDirection='←'elseif(angle>=225&&angle<315)this.swipeDirection='↓'elsethis.swipeDirection='→'

把 360° 分成四个 90° 的扇区,每个扇区对应一个方向。比之前的"左/右"二分法更精细。

日志面板

Column(){Text('手势日志').fontSize(13).fontWeight(FontWeight.Bold).margin({bottom:8})ForEach(this.gestureLog,(log:string)=>{Text(log).fontSize(11).fontColor('#999999').margin({bottom:2})})}.width('100%').margin({top:16}).padding(12).backgroundColor('#F8F8F8').borderRadius(8)

时间戳 + 手势名称的列表,最多 8 条。类似开发者工具的日志面板。

踩坑记录

问题原因解决
双击不触发被单击"抢走"了双击写在单击前面(Exclusive 模式按顺序判定)
拖拽和滑动冲突两者都是水平/垂直滑动用速度区分:SwipeGesture 有速度阈值
缩放后面板数字跳动缩放影响了布局只影响测试区域,不影响统计面板

多次调用.gesture()时,手势之间的优先级取决于绑定顺序。先绑定的优先级高。如果发现某种手势"死活触发不了",检查一下是不是被另一个手势"抢"了。

写在最后

这个综合案例把手势系列的所有知识点串在了一起。核心设计思路是"分层管理"——互斥的放一层,并行的放一层,独立的放一层。配合 Grid 看板和日志面板,不仅能验证手势是否正常工作,还能直观看到每种手势的触发情况。

做手势密集的界面时,建议也做一个类似的调试面板,开发效率会高很多。