
回复
在鸿蒙应用开发中,手势交互系统是实现自然人机对话的关键技术。通过识别用户的点击、滑动、拖拽等手势行为,开发者能够构建符合直觉的交互体验,如图片编辑中的多点缩放、列表项的滑动删除、组件的自由拖拽等场景。本文将系统解构鸿蒙手势处理的核心机制、手势类型及工程实践技巧,帮助开发者掌握从基础手势到复杂组合手势的全流程实现方法。
鸿蒙手势系统基于识别器 - 事件回调双层架构实现:
onAction(event: Callback<GestureEvent>): TapGestureHandler
Tap手势识别成功回调。
onAction(event: Callback<GestureEvent>): LongPressGestureHandler
LongPress手势识别成功回调。
onActionEnd(event: Callback<GestureEvent>): LongPressGestureHandler
LongPress手势识别成功,最后一根手指抬起后触发回调。
onActionCancel(event: Callback<void>): LongPressGestureHandler
LongPress手势识别成功,接收到触摸取消事件触发回调。不返回手势事件信息。
onActionCancel(event: Callback<GestureEvent>): LongPressGestureHandler
LongPress手势识别成功,接收到触摸取消事件触发回调。与onActionCancel接口相比,此接口返回手势事件信息。
onActionStart(event: Callback<GestureEvent>): PanGestureHandler
Pan手势识别成功回调。
onActionUpdate(event: Callback<GestureEvent>): PanGestureHandler
Pan手势移动过程中回调。
onActionEnd(event: Callback<GestureEvent>): PanGestureHandler
Pan手势识别成功,手指抬起后触发回调。
onActionCancel(event: Callback<void>): PanGestureHandler
Pan手势识别成功,接收到触摸取消事件触发回调。不返回手势事件信息。
onActionCancel(event: Callback<GestureEvent>): PanGestureHandler
Pan手势识别成功,接收到触摸取消事件触发回调。与onActionCancel接口相比,此接口返回手势事件信息。
onActionStart(event: Callback<GestureEvent>): PinchGestureHandler
Pinch手势识别成功回调。
onActionUpdate(event: Callback<GestureEvent>): PinchGestureHandler
Pinch手势移动过程中回调。
onActionEnd(event: Callback<GestureEvent>): PinchGestureHandler
Pinch手势识别成功,手指抬起后触发回调。
onActionCancel(event: Callback<void>): PinchGestureHandler
Pinch手势识别成功,接收到触摸取消事件触发回调。不返回手势事件信息。
onActionCancel(event: Callback<GestureEvent>): PinchGestureHandler
Pinch手势识别成功,接收到触摸取消事件触发回调。与onActionCancel接口相比,此接口返回手势事件信息。
onActionStart(event: Callback<GestureEvent>): RotationGestureHandler
Rotation手势识别成功回调。
onActionUpdate(event: Callback<GestureEvent>): RotationGestureHandler
Rotation手势移动过程中回调。
onActionEnd(event: Callback<GestureEvent>): RotationGestureHandler
Rotation手势识别成功,手指抬起后触发回调。
onActionCancel(event: Callback<void>): RotationGestureHandler
Rotation手势识别成功,接收到触摸取消事件触发回调。不返回手势事件信息。
onActionCancel(event: Callback<GestureEvent>): RotationGestureHandler
Rotation手势识别成功,接收到触摸取消事件触发回调。与onActionCancel相比,此接口返回手势事件信息。
onAction(event: Callback<GestureEvent>): SwipeGestureHandlerOptions
Swipe手势识别成功回调。
通过GestureGroup
实现多手势协同,支持三种模式:
手势按注册顺序依次识别,前一手势成功后才触发下一手势
@Entry
@Component
struct SequenceGestureDemo {
@State offsetX: number = 0
@State offsetY: number = 0
@State pressCount: number = 0
build() {
Text('长按后拖拽')
.fontSize(28)
.translate({ x: this.offsetX, y: this.offsetY })
.width(300)
.height(250)
.gesture(
GestureGroup(GestureMode.Sequence,
// 长按手势(可重复触发)
LongPressGesture({ repeat: true })
.onAction(() => this.pressCount++)
.onActionEnd(() => console.log('长按结束')),
// 平移手势(长按后触发)
PanGesture()
.onActionStart(() => console.log('拖拽开始'))
.onActionUpdate((event) => {
this.offsetX += event.offsetX
this.offsetY += event.offsetY
})
)
)
}
}
多手势同时识别,互不影响(如平移时允许点击)
@Entry
@Component
struct ParallelGestureDemo {
@State clickCount: number = 0
@State panOffsetX: number = 0
@State panOffsetY: number = 0
build() {
Column() {
Image($r('app.media.startIcon'))
.width('100%')
.height('100%')
.gesture(
GestureGroup(GestureMode.Parallel,
TapGesture({ count: 1 })
.onAction(() => this.clickCount++),
PanGesture()
.onActionUpdate((event) => {
this.panOffsetX += event.offsetX
this.panOffsetY += event.offsetY
})
)
)
.translate({ x: this.panOffsetX, y: this.panOffsetY })
}
}
}
手势同时识别,任一成功即终止其他手势
@Entry
@Component
struct ExclusiveGestureDemo {
@State clickFlag: boolean = false
@State swipeFlag: boolean = false
build() {
Column() {
if (this.clickFlag) {
Text('点击手势识别')
} else if (this.swipeFlag) {
Text('左滑手势识别')
}
}
.width('100%')
.height('100%')
.gesture(
GestureGroup(GestureMode.Exclusive,
TapGesture({ count: 1 })
.onAction(() => {
this.clickFlag = true
this.swipeFlag = false
}),
SwipeGesture({ direction: SwipeDirection.Horizontal })
.onAction(() => {
this.swipeFlag = true
this.clickFlag = false
})
)
)
}
}
父组件通过priorityGesture
优先捕获手势
@Entry
@Component
struct PriorityGestureDemo {
@State parentClick: number = 0
@State childClick: number = 0
build() {
Column() {
Text('父组件' + this.parentClick)
.width('50%')
.height('50%')
.backgroundColor(Color.Pink)
Text('子组件' + this.childClick)
.width('50%')
.height('50%')
.backgroundColor(Color.White)
.gesture(
TapGesture({ count: 1 })
.onAction(() => {
this.childClick++
})
)
}
.width('100%')
.height('100%')
.priorityGesture(
TapGesture({ count: 1 })
.onAction(() => {
this.parentClick++
})
)
.backgroundColor(Color.Gray)
}
}
通过手动调用父组件逻辑实现类似冒泡效果
@Entry
@Component
struct BubbleGestureDemo {
@State itemClick: number = 0
@State listClick: number = 0
handleItemClick() {
this.itemClick++
this.handleListClick() // 手动触发父逻辑
}
handleListClick() {
this.listClick++
}
build() {
Column() {
Text('父' + this.listClick)
.width('50%')
.height('30%')
.backgroundColor(Color.Pink)
Text('子' + this.itemClick)
.width('50%')
.height('30%')
.backgroundColor(Color.White)
.gesture(
TapGesture({ count: 1 })
.onAction(() => this.handleItemClick())
)
}
.width('100%')
.height('100%')
.gesture(
TapGesture({ count: 1 })
.onAction(() => this.handleListClick())
)
.backgroundColor(Color.Gray)
}
}
@Entry
@Component
struct StickerDragApp {
@State stickerX: number = 50 // 初始X坐标
@State stickerY: number = 50 // 初始Y坐标
build() {
Column() {
// 可拖拽贴纸
Image($r("app.media.startIcon"))
.width(80)
.height(80)
.translate({ x: this.stickerX, y: this.stickerY })
.gesture(
PanGesture()
.onActionUpdate((event) => {
// 累加位移并限制边界
this.stickerX = Math.max(
0,
Math.min(event.offsetX, 300)
)
this.stickerY = Math.max(
0,
Math.min(event.offsetY, 500)
)
})
)
}
.backgroundColor("#FFE4B5")
.width("100%")
.height("100%")
}
}
@Entry
@Component
struct SwipeDeleteApp {
@State messages: string[] = ['通知1', '通知2', '通知3', '通知4']
@State itemOffsets: number[] = [] // 改为数组存储偏移量
@State isDeleting: boolean[] = [] // 改为数组存储删除状态
aboutToAppear() {
// 初始化状态数组
this.itemOffsets = new Array(this.messages.length).fill(0)
this.isDeleting = new Array(this.messages.length).fill(false)
}
// 处理滑动更新
handleSwipe(index: number, event: GestureEvent) {
// 限制偏移量范围,避免过度滑动
this.itemOffsets[index] = Math.max(-200, Math.min(0, event.offsetX))
}
// 处理滑动结束
handleSwipeEnd(index: number, event: GestureEvent) {
// 向左滑动超过80px时执行删除(降低触发门槛)
if (this.itemOffsets[index] < -80) {
this.startDeleteAnimation(index)
} else {
this.resetPosition(index)
}
}
// 启动删除动画
startDeleteAnimation(index: number) {
this.isDeleting[index] = true
setTimeout(() => {
// 删除元素并更新状态数组
this.messages.splice(index, 1)
this.itemOffsets.splice(index, 1)
this.isDeleting.splice(index, 1)
}, 300)
}
// 复位动画
resetPosition(index: number) {
animateTo({
duration: 300,
curve: Curve.EaseOut
}, () => {
this.itemOffsets[index] = 0
})
}
build() {
Column() {
List() {
ForEach(this.messages, (item: string, index) => {
ListItem() {
Row() {
Text(item)
.flexGrow(1)
.padding(16)
if (this.isDeleting[index]) {
Text('删除中...')
.fontColor(Color.Red)
.padding(16)
} else {
Button('删除')
.backgroundColor(Color.Red)
.width(80)
.onClick(() => this.startDeleteAnimation(index))
}
}
.width('100%')
}
.gesture(
PanGesture({ direction: PanDirection.All })
.onActionUpdate((event: GestureEvent) => this.handleSwipe(index, event))
.onActionEnd((event: GestureEvent) => this.handleSwipeEnd(index, event))
)
.offset({ x: this.itemOffsets[index] })
}, (_item: string, index) => index.toString())
}
.width('100%')
.layoutWeight(1)
}
.width('100%')
.height('100%')
.backgroundColor(Color.White)
}
}
@Entry
@Component
struct OptimizedGestureApp {
@State offsetX: number = 0
@State offsetY: number = 0
private lastOffsetX: number = 0 // 记录上次偏移量
private lastOffsetY: number = 0
// 使用帧动画优化高频更新
handlePanUpdate(event: GestureEvent) {
// 计算增量偏移(带0.8阻尼系数)
const deltaX = (event.offsetX - this.lastOffsetX) * 0.8
const deltaY = (event.offsetY - this.lastOffsetY) * 0.8
// 累加偏移量并四舍五入到整数
this.offsetX += Math.round(deltaX)
this.offsetY += Math.round(deltaY)
// 更新最后记录位置
this.lastOffsetX = event.offsetX
this.lastOffsetY = event.offsetY
}
build() {
Column() {
// 可拖动矩形
Column()
.width(200)
.height(200)
.backgroundColor(Color.Blue)
.gesture(
PanGesture()
.onActionStart(() => {
// 手势开始时重置记录点
this.lastOffsetX = 0
this.lastOffsetY = 0
})
.onActionUpdate((event: GestureEvent) => {
this.handlePanUpdate(event)
})
)
.translate({ x: this.offsetX, y: this.offsetY })
}
.width('100%')
.height('100%')
.justifyContent(FlexAlign.Center)
}
}
@Entry
@Component
struct ConflictResolutionApp {
@State scaleXY: number = 1
@State panX: number = 0
@State panY: number = 0
build() {
Column() {
Image($r("app.media.startIcon"))
.width(30)
.height(30)
.scale({ x: this.scaleXY, y: this.scaleXY })
.translate({ x: this.panX, y: this.panY })
.gesture(
GestureGroup(GestureMode.Parallel, // 并行模式避免冲突
PinchGesture()
.onActionUpdate((event) => {
this.scaleXY = Math.max(0.5, Math.min(2, this.scaleXY * event.scale))
}),
PanGesture()
.onActionUpdate((event) => {
this.panX += event.offsetX
this.panY += event.offsetY
})
)
)
}
.width('100%')
.height('100%')
}
}
// 兼容不同API版本的手势逻辑
#if (API >= 9)
// API 9+新特性
let advancedGesture = PanGesture({
direction: PanDirection.All,
minDistance: 10
})
.onActionUpdate((event) => {
// 新回调参数处理
})
#else
// 旧版本兼容逻辑
let legacyGesture = PanGesture()
.onActionUpdate((event) => {
// 旧回调处理
})
#endif
@Entry
@Component
struct GestureDebugApp {
@State count: number = 0
build() {
Button('测试手势')
.width(200)
.height(80)
.backgroundColor(Color.Green)
.gesture(
TapGesture({ count: 2 }) // 双击手势
.onAction((event) => {
this.count++
console.log(`双击事件:
坐标(x,y): (${event.offsetX}, ${event.offsetY})
点击次数: ${this.count}
时间戳: ${event.timestamp}`)
})
)
}
}
鸿蒙手势处理系统通过标准化接口与灵活组合机制,提供了从基础交互到复杂手势的完整解决方案。开发者需重点掌握:
建议从基础案例入手,逐步尝试复杂交互场景,结合官方模拟器的手势调试工具(如多点触控模拟)验证效果。随着鸿蒙生态的演进,手势处理将与 AI 交互、多设备协同深度融合,成为全场景应用的核心竞争力。