公司动态

HarmonyOS7 手势交互页实战:点击、长按、拖拽 不只是会用,还要用得顺手

📅 2026/7/18 5:20:56
HarmonyOS7 手势交互页实战:点击、长按、拖拽 不只是会用,还要用得顺手
文章目录每个手势都要有明确反馈拖拽和捏合要注意收口手势冲突别靠猜完整代码写在最后手势交互最容易写成“能触发就行”。但真到页面里点击、双击、长按、滑动、拖拽、捏合放在一起重点就不是 API 名字而是每个区域到底响应什么、状态怎么反馈。这个页面把几种常见手势分开放在不同卡片里学习成本比较低。你可以先跑起来感受交互再回头看每个gesture()里面绑定了什么状态。每个手势都要有明确反馈单击和双击用计数器反馈长按用背景色和文案反馈滑动用方向文字反馈拖拽用偏移值反馈捏合用缩放比例反馈。这样的示例才有参考价值因为它不是只证明“事件触发了”而是告诉用户当前操作产生了什么结果。页面里的状态也围绕反馈来设计tapCount、doubleTapCount、longPressActive、swipeDir、panDeltaX、panDeltaY、pinchScale都不是摆设它们直接绑定到 UI 上。拖拽和捏合要注意收口拖拽区域里PanGesture在onActionUpdate里更新偏移松手后用animateTo回到中心。捏合也是一样更新时限制缩放范围结束后恢复到 1。这个“收口”很重要。没有收口的手势很容易把元素拖到奇怪位置或者把缩放比例留在不可控状态。写交互时开始、更新、结束三个阶段都要想清楚。手势冲突别靠猜实际项目里不建议把所有手势堆在同一个组件上。比如列表项里既要点击进入详情又要长按打开菜单还要左右滑删除就必须明确触发区域和手势优先级。这个案例把每种手势拆开讲正好适合先理解单个手势的行为。完整代码下面是整理后的完整代码入口组件名已经换成和当前场景匹配的名字放进自己的页面目录后可以按项目路由规则接入。// 手势处理点击、长按、拖拽interfaceDragItem{id:numberlabel:stringcolor:stringbgColor:stringx:numbery:number}EntryComponentstruct GestureInteractionPage{StatetapCount:number0StatedoubleTapCount:number0StatelongPressActive:booleanfalseStatelongPressMsg:string长按此处StatepanDeltaX:number0StatepanDeltaY:number0StatepanVelocityX:number0StateballColor:string#2563EBStateswipeDir:string等待滑动StatepinchScale:number1StaterotationAngle:number0privatecolorList:string[][#2563EB,#EF4444,#059669,#D97706,#7C3AED,#EC4899]privatecolorIdx:number0build(){Scroll(){Column({space:0}){// 标题Text(手势处理).fontSize(20).fontWeight(FontWeight.Bold).fontColor(#111827).width(100%).padding({left:20,right:20,top:20,bottom:4})Text(Tap / LongPress / Pan / Swipe / Pinch).fontSize(13).fontColor(#6B7280).width(100%).padding({left:20,right:20,bottom:16})// 一、点击与双击Column({space:12}){Text(一、点击 / 双击手势).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#374151).width(100%)Row({space:12}){// 单击Column({space:8}){Text().fontSize(32).width(80).height(80).textAlign(TextAlign.Center).backgroundColor(#EFF6FF).borderRadius(16).gesture(TapGesture({count:1}).onAction((){this.tapCount}))Text(单击 ×this.tapCount.toString()).fontSize(12).fontColor(#2563EB)}.alignItems(HorizontalAlign.Center)// 双击Column({space:8}){Text().fontSize(24).width(80).height(80).textAlign(TextAlign.Center).backgroundColor(#D1FAE5).borderRadius(16).gesture(TapGesture({count:2}).onAction((){this.doubleTapCount}))Text(双击 ×this.doubleTapCount.toString()).fontSize(12).fontColor(#059669)}.alignItems(HorizontalAlign.Center)// 变色点击Column({space:8}){Text().fontSize(32).width(80).height(80).textAlign(TextAlign.Center).backgroundColor(this.ballColor).borderRadius(40).gesture(TapGesture({count:1}).onAction((){this.colorIdx(this.colorIdx1)%this.colorList.lengththis.ballColorthis.colorList[this.colorIdx]}))Text(点击变色).fontSize(12).fontColor(#6B7280)}.alignItems(HorizontalAlign.Center)}.width(100%).justifyContent(FlexAlign.SpaceAround).padding(14).backgroundColor(#F9FAFB).borderRadius(10)}.width(100%).padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 二、长按手势Column({space:12}){Text(二、长按手势 LongPress).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#374151).width(100%)Column({space:8}){Stack({alignContent:Alignment.Center}){Row().width(100%).height(72).borderRadius(10).backgroundColor(this.longPressActive?#FEF3C7:#F9FAFB).borderWidth(this.longPressActive?2:1).borderColor(this.longPressActive?#D97706:#E5E7EB).gesture(LongPressGesture({duration:500}).onAction((event:GestureEvent){this.longPressActivetruethis.longPressMsg长按触发松开手指}).onActionEnd((){this.longPressActivefalsethis.longPressMsg已松开长按 500ms 触发}))Text(this.longPressMsg).fontSize(14).fontColor(this.longPressActive?#D97706:#9CA3AF).fontWeight(this.longPressActive?FontWeight.Bold:FontWeight.Normal)}.width(100%)}}.width(100%).padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 三、滑动手势Column({space:12}){Text(三、滑动方向 Swipe).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#374151).width(100%)Column({space:8}){Stack({alignContent:Alignment.Center}){Row().width(100%).height(100).borderRadius(10).backgroundColor(#F5F3FF).borderWidth(1).borderColor(#DDD6FE).gesture(SwipeGesture({direction:SwipeDirection.All}).onAction((event:GestureEvent){constangleevent.angleif(angle-45angle45){this.swipeDir→ 向右滑动}elseif(angle135||angle-135){this.swipeDir← 向左滑动}elseif(angle45angle135){this.swipeDir↓ 向下滑动}else{this.swipeDir↑ 向上滑动}}))Column({space:4}){Text( 在此区域内滑动).fontSize(13).fontColor(#9CA3AF)Text(this.swipeDir).fontSize(16).fontWeight(FontWeight.Bold).fontColor(#7C3AED)}.alignItems(HorizontalAlign.Center)}.width(100%)}}.width(100%).padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 四、拖拽手势位移Column({space:12}){Text(四、拖拽手势 Pan).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#374151).width(100%)Column({space:8}){Stack({alignContent:Alignment.Center}){Row().width(100%).height(140).borderRadius(10).backgroundColor(#F3F4F6)Text().fontSize(32).translate({x:this.panDeltaX,y:this.panDeltaY}).gesture(PanGesture({direction:PanDirection.All}).onActionStart((){}).onActionUpdate((event:GestureEvent){this.panDeltaXevent.offsetXthis.panDeltaYevent.offsetY}).onActionEnd((){animateTo({duration:300,curve:Curve.EaseOut},(){this.panDeltaX0this.panDeltaY0})}))}.width(100%)Text(偏移X${this.panDeltaX.toFixed(0)}Y${this.panDeltaY.toFixed(0)}松手自动回中).fontSize(12).fontColor(#9CA3AF)}}.width(100%).padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:16})// 五、捏合缩放Column({space:12}){Text(五、捏合缩放 Pinch).fontSize(14).fontWeight(FontWeight.Bold).fontColor(#374151).width(100%)Column({space:8}){Stack({alignContent:Alignment.Center}){Row().width(100%).height(140).borderRadius(10).backgroundColor(#ECFDF5)Text().fontSize(48).scale({x:this.pinchScale,y:this.pinchScale}).gesture(PinchGesture().onActionUpdate((event:GestureEvent){this.pinchScaleMath.max(0.3,Math.min(3,event.scale))}).onActionEnd((){animateTo({duration:300,curve:Curve.EaseOut},(){this.pinchScale1})}))}.width(100%)Text(缩放比${this.pinchScale.toFixed(2)}双指捏合/展开松手复原).fontSize(12).fontColor(#9CA3AF)}}.width(100%).padding({left:20,right:20}).alignItems(HorizontalAlign.Start).margin({bottom:24})}}.width(100%).height(100%).backgroundColor(#FFFFFF)}}写在最后这个例子不复杂但很适合反复看。很多 ArkUI 页面写不顺其实不是组件不会用而是状态、结构和反馈没有放在一起设计。