本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、鸿蒙手势体系概览
手势类型 | 触发条件 | 典型应用场景 |
---|---|---|
点击(Click) | 手指按下并抬起 | 按钮点击、图标选择 |
长按(LongPress) | 持续按压超过500ms | 弹出菜单、拖动准备 |
拖拽(Pan) | 按压后移动 | 滑动列表、元素拖动 |
捏合(Pinch) | 双指缩放 | 图片放大/缩小 |
旋转(Rotation) | 双指旋转 | 图片旋转 |
二、基础手势使用
1. 点击手势(Click)
@Entry
@Component
struct ClickExample {
@State count: number = 0;
build() {
Column() {
Text(`点击次数: ${this.count}`)
.fontSize(20)
.margin(10)
Button('点我')
.onClick(() => { // 内置点击事件
this.count++;
})
// 使用Gesture单独实现点击
Text('自定义点击区域')
.gesture(
TapGesture()
.onAction(() => {
this.count++;
console.log('Tap触发');
})
)
}
}
}
参数说明:
onAction
:点击完成时触发(手指抬起)
2. 长按手势(LongPress)
Text('长按触发菜单')
.gesture(
LongPressGesture()
.onActionStart(() => console.log('开始长按')) // 按压时
.onAction(() => showContextMenu()) // 长按完成
.onActionEnd(() => console.log('结束')) // 手指抬起
)
关键属性:
duration
:自定义长按阈值(默认500ms)LongPressGesture({ duration: 1000 }) // 改为1秒触发
三、高级手势控制
1. 拖拽手势(Pan)
@Entry
@Component
struct DragExample {
@State offsetX: number = 0;
@State offsetY: number = 0;
build() {
Stack() {
Image($r('app.media.icon'))
.width(100)
.height(100)
.offset({ x: this.offsetX, y: this.offsetY })
.gesture(
PanGesture()
.onActionStart(() => console.log('开始拖动'))
.onActionUpdate((event: GestureEvent) => {
this.offsetX += event.offsetX;
this.offsetY += event.offsetY;
})
.onActionEnd(() => console.log('拖动结束'))
)
}
.height('100%')
.width('100%')
}
}
事件对象:
event.offsetX/Y
:相对于起点的位移event.globalX/Y
:全局坐标
2. 捏合缩放(Pinch)
@State scale: number = 1;
Image($r('app.media.photo'))
.scale({ x: this.scale, y: this.scale })
.gesture(
PinchGesture()
.onActionUpdate((event: GestureEvent) => {
this.scale *= event.scale; // event.scale为缩放因子
})
)
四、手势组合与冲突解决
1. 手势并行处理
Text('多手势支持')
.gesture(
GestureGroup(
GestureMode.Parallel, // 并行模式
[
TapGesture().onAction(() => console.log('点击')),
LongPressGesture().onAction(() => console.log('长按'))
]
)
)
2. 手势互斥(优先级控制)
GestureGroup(
GestureMode.Exclusive, // 互斥模式
[
PanGesture().onActionUpdate((e) => { /* 拖拽逻辑 */ }),
LongPressGesture().onAction(() => console.log('长按优先'))
]
)
五、自定义手势开发
1. 监听原始触摸事件
@Entry
@Component
struct TouchExample {
@State touchX: number = 0;
@State touchY: number = 0;
build() {
Column()
.onTouch((event: TouchEvent) => {
if (event.type === TouchType.Down) {
this.touchX = event.touches[0].x;
this.touchY = event.touches[0].y;
}
})
}
}
事件类型:
TouchType.Down
:按下TouchType.Move
:移动TouchType.Up
:抬起
2. 实现双击检测
let lastTapTime: number = 0;
Text('双击区域')
.gesture(
TapGesture({ count: 2 }) // 监听双击
.onAction(() => console.log('双击触发'))
)
六、性能优化与常见问题
1. 手势拦截优化
Column()
.gesture(
PanGesture()
.onActionUpdate((e) => {
if (Math.abs(e.offsetX) > 10) {
// 水平移动超过10vp才触发
handlePanMove(e);
}
})
)
2. 常见问题解决
问题 | 原因 | 解决方案 |
---|---|---|
手势无响应 | 父组件拦截触摸事件 | 检查父容器的touchable 属性 |
拖拽卡顿 | 频繁触发状态更新 | 使用@Prop 代替@State 减少渲染 |
多手势冲突 | 未正确设置GestureGroup模式 | 明确使用Parallel 或Exclusive |
七、完整示例:图片查看器
@Entry
@Component
struct ImageViewer {
@State scale: number = 1;
@State offsetX: number = 0;
@State offsetY: number = 0;
build() {
Stack() {
Image($r('app.media.pic'))
.scale({ x: this.scale, y: this.scale })
.offset({ x: this.offsetX, y: this.offsetY })
.gesture(
GestureGroup(
GestureMode.Parallel,
[
// 双击复位
TapGesture({ count: 2 })
.onAction(() => {
this.scale = 1;
this.offsetX = 0;
this.offsetY = 0;
}),
// 双指缩放
PinchGesture()
.onActionUpdate((e) => {
this.scale *= e.scale;
}),
// 单指拖动
PanGesture()
.onActionUpdate((e) => {
this.offsetX += e.offsetX;
this.offsetY += e.offsetY;
})
]
)
)
}
.width('100%')
.height('100%')
}
}