本文同步发表于我的微信公众号,微信搜索 程语新视界 即可关注,每个工作日都有文章更新
一、手势系统核心概念
1. 手势类型与触发条件
手势类型 | 触发条件 | 典型应用场景 |
---|---|---|
Tap(点击) | 快速按下并释放(<300ms) | 按钮点击、选择操作 |
LongPress(长按) | 持续按压(≥500ms) | 上下文菜单、拖拽准备 |
Pan(平移) | 按压后移动(≥5vp) | 拖拽、滑动操作 |
Pinch(缩放) | 双指捏合/扩张 | 图片缩放、地图缩放 |
Rotation(旋转) | 双指旋转 | 图片旋转、3D模型控制 |
Swipe(快速滑动) | 快速单向移动(速度>100vp/s) | 翻页、删除操作 |
2. 手势识别流程
A[触摸事件] --> B(命中测试)
B --> C{是否在目标区域}
C -->|是| D[手势识别器分析]
C -->|否| E[事件丢弃]
D --> F[触发回调]
二、基础使用与API详解
1. 单手势绑定(声明式API)
@Entry
@Component
struct GestureExample {
@State scale: number = 1.0;
@State angle: number = 0;
build() {
Column() {
Image($r('app.media.logo'))
.scale({ x: this.scale, y: this.scale })
.rotate({ angle: this.angle })
// 绑定缩放手势
.gesture(
Gesture(GestureType.Pinch)
.onActionUpdate((event: GestureEvent) => {
this.scale *= event.scale; // event.scale为缩放比例
})
)
// 绑定旋转手势
.gesture(
Gesture(GestureType.Rotation)
.onActionUpdate((event: GestureEvent) => {
this.angle += event.angle; // event.angle为旋转弧度
})
)
}
}
}
2. 多手势组合(GestureGroup)
import { Gesture, GestureGroup } from '@ohos.gesture';
private complexGesture = new GestureGroup({
gestures: [
new Gesture(GestureType.Pan)
.onActionStart(() => console.log('拖拽开始'))
.onActionUpdate((event) => {
console.log(`位移: X=${event.offsetX}, Y=${event.offsetY}`);
}),
new Gesture(GestureType.LongPress)
.onActionEnd(() => console.log('长按触发'))
],
priority: GesturePriority.Parallel // 并行识别
});
// 绑定到组件
Image($r('app.media.map'))
.gesture(this.complexGesture)
优先级模式:
Parallel
:并行处理(默认)Exclusive
:互斥(高优先级手势优先)Sequence
:序列(按添加顺序触发)
三、高级功能实战
1. 自定义手势识别(原始事件处理)
@State startTime: number = 0;
@State startPoint: {x: number, y: number} = {x: 0, y: 0};
build() {
Column()
.onTouch((event: TouchEvent) => {
switch (event.type) {
case TouchType.Down:
this.startTime = Date.now();
this.startPoint = {x: event.touches[0].x, y: event.touches[0].y};
break;
case TouchType.Up:
const duration = Date.now() - this.startTime;
const distance = Math.sqrt(
Math.pow(event.touches[0].x - this.startPoint.x, 2) +
Math.pow(event.touches[0].y - this.startPoint.y, 2)
);
// 自定义双击识别(间隔<300ms且位移<5vp)
if (duration < 300 && distance < 5) {
console.log('双击触发');
}
break;
}
})
}
2. 手势冲突解决(Exclusive组合)
private swipeAndTap = new GestureGroup({
gestures: [
new Gesture(GestureType.Swipe) // 高优先级
.onActionEnd(() => console.log('滑动生效')),
new Gesture(GestureType.Tap) // 低优先级
.onActionEnd(() => console.log('点击生效'))
],
priority: GesturePriority.Exclusive
});
// 绑定到可滑动列表项
ListItem()
.gesture(this.swipeAndTap)
3. 手势动画(弹性效果)
import curve from '@ohos.curves';
@State offsetX: number = 0;
private panGesture = new Gesture(GestureType.Pan)
.onActionUpdate((event: GestureEvent) => {
this.offsetX = event.offsetX;
})
.onActionEnd(() => {
// 使用弹性曲线回弹
animateTo({
duration: 500,
curve: curve.springMotion()
}, () => {
this.offsetX = 0;
})
});
// 应用动画
Image($r('app.media.card'))
.translate({ x: this.offsetX })
.gesture(this.panGesture)
四、调试与性能优化
1. 性能优化建议
场景 | 优化措施 |
---|---|
高频手势(如绘图) | 使用@ohos.worker 分线程处理数据 |
复杂手势树 | 减少嵌套GestureGroup层级 |
内存管理 | 在aboutToDisappear 中取消监听 |
2. 常见问题解决
问题 | 解决方案 |
---|---|
手势未触发 | 检查组件尺寸/是否被其他手势拦截 |
动画卡顿 | 使用硬件加速(hardwareAccelerated ) |
多指手势识别错误 | 通过event.fingerId 区分触点 |
五、案例:图片查看器
@Entry
@Component
struct ImageViewer {
@State scale: number = 1.0;
@State offsetX: number = 0;
@State offsetY: number = 0;
private gestureGroup = new GestureGroup({
gestures: [
new Gesture(GestureType.Pinch)
.onActionUpdate((event) => {
this.scale *= event.scale;
}),
new Gesture(GestureType.Pan)
.onActionUpdate((event) => {
this.offsetX += event.offsetX;
this.offsetY += event.offsetY;
}),
new Gesture(GestureType.DoubleTap)
.onActionEnd(() => {
animateTo({ duration: 200 }, () => {
this.scale = this.scale === 1.0 ? 2.0 : 1.0;
})
})
]
});
build() {
Stack() {
Image($r('app.media.photo'))
.scale({ x: this.scale, y: this.scale })
.translate({ x: this.offsetX, y: this.offsetY })
.gesture(this.gestureGroup)
// 复位按钮
Button('重置')
.onClick(() => {
animateTo({ duration: 300 }, () => {
this.scale = 1.0;
this.offsetX = 0;
this.offsetY = 0;
})
})
}
}
}