ArkUI 手势冲突:别等用户滑不动了才处理
【摘要】 今天我们看手势冲突里最基础的裁决入口。 很多手势问题不是“代码不生效”,而是两个动作都觉得自己应该生效。比如横向拖动卡片、纵向滚动列表、子组件点击,这些意图如果不拆开,用户一滑不动就会很烦。 参考文档: https://developer.huawei.com/consumer/cn/doc/harmonyosguid
ArkUI 手势冲突:别等用户滑不动了才处理
今天我们看手势冲突里最基础的裁决入口。
很多手势问题不是“代码不生效”,而是两个动作都觉得自己应该生效。比如横向拖动卡片、纵向滚动列表、子组件点击,这些意图如果不拆开,用户一滑不动就会很烦。
参考文档:
- https://developer.huawei.com/consumer/cn/doc/harmonyos-guides/arkts-interaction-development-guide-overview
用裁决继续或拒绝
.onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent): GestureJudgeResult => {
return this.rejectPan ? GestureJudgeResult.REJECT : GestureJudgeResult.CONTINUE
})
CONTINUE 表示继续交给系统识别,REJECT 表示当前组件的这个手势不要继续。Demo 用开关模拟这件事,方便你直接看到差别。
这里我们还会在 onActionUpdate 里补一层兜底:拒绝状态下不再写入 panX。原因很现实,裁决回调和手势更新回调在不同设备、不同识别阶段可能不是你想象中的“先后永远固定”。真正写位移的地方也守住,Demo 才不会出现“显示拒绝了,但卡片还在动”的错觉。
识别器裁决只做观察
.onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, recognizers: Array<GestureRecognizer>): GestureJudgeResult => {
this.recognizerText = `识别器数量 ${recognizers.length}`
return GestureJudgeResult.CONTINUE
})
这一篇只做轻量观察,不做复杂业务。真要处理列表嵌套拖拽,先把“谁应该赢”定清楚,再写裁决逻辑。
你有没有遇到过横滑卡片和纵向列表互相抢手势?如果有,下一步就很适合把这个例子扩成列表场景。
完整示例代码
文件位置:entry/src/main/ets/pages/arkUI/interaction/ArkUIGestureConflictDemo.ets
@Entry
@Component
struct ArkUIGestureConflictDemo {
@State rejectPan: boolean = false
@State judgeText: string = '拖动卡片,先看默认识别流程'
@State recognizerText: string = '识别器裁决还没有触发'
@State parallelText: string = '内置识别器并行判断还没有触发'
@State panX: number = 0
private showTip(message: string): void {
this.getUIContext().getPromptAction().showToast({
message: message,
duration: 1200,
bottom: 120
})
}
@Builder
sectionTitle(title: string, desc: string) {
Column({ space: 6 }) {
Text(title)
.fontSize(19)
.fontWeight(FontWeight.Bold)
.fontColor('#111827')
Text(desc)
.fontSize(13)
.fontColor('#6B7280')
.lineHeight(20)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
}
build() {
Scroll() {
Column({ space: 22 }) {
Column({ space: 8 }) {
Text('ArkUI 手势冲突')
.fontSize(28)
.fontWeight(FontWeight.Bold)
.fontColor('#111827')
Text('别等用户滑不动了才想起来处理')
.fontSize(16)
.fontColor('#4B5563')
Text('这一页只做轻量裁决演示:什么时候继续识别,什么时候拒绝当前手势。真正复杂的列表嵌套和业务规则,应该先把意图拆清楚。')
.fontSize(14)
.fontColor('#6B7280')
.lineHeight(22)
}
.width('100%')
.alignItems(HorizontalAlign.Start)
this.sectionTitle('先切换裁决结果', '打开开关后,onGestureJudgeBegin 会拒绝当前拖动手势;关闭后继续识别。')
Toggle({ type: ToggleType.Switch, isOn: this.rejectPan })
.onChange((isOn: boolean) => {
this.rejectPan = isOn
this.judgeText = isOn ? '当前策略:拒绝拖动手势' : '当前策略:允许拖动手势'
if (isOn) {
this.panX = 0
}
this.showTip(isOn ? '将拒绝拖动' : '允许拖动')
})
Column({ space: 12 }) {
Text('可拖动卡片')
.fontSize(19)
.fontWeight(FontWeight.Bold)
.fontColor('#FFFFFF')
Text('横向拖动试试。裁决拒绝时,拖动反馈不会继续推进。')
.fontSize(13)
.fontColor('#DBEAFE')
.lineHeight(20)
}
.width('100%')
.padding(18)
.borderRadius(18)
.backgroundColor('#2563EB')
.translate({ x: this.panX, y: 0 })
.gesture(
PanGesture({ fingers: 1, direction: PanDirection.Horizontal, distance: 5 })
.tag('conflict-pan')
.onActionUpdate((event: GestureEvent) => {
if (this.rejectPan) {
this.panX = 0
this.judgeText = '拖动已被拒绝:卡片保持原位'
return
}
this.panX = event.offsetX
this.judgeText = `拖动中:x ${Math.round(event.offsetX)}`
})
.onActionEnd(() => {
this.panX = 0
this.judgeText = this.rejectPan ? '拖动已拒绝:没有产生位移结果' : '拖动结束:卡片回到原位'
this.showTip(this.rejectPan ? '拖动已拒绝' : '拖动结束')
})
)
.onGestureJudgeBegin((gestureInfo: GestureInfo, event: BaseGestureEvent): GestureJudgeResult => {
this.judgeText = `onGestureJudgeBegin:${this.rejectPan ? 'REJECT' : 'CONTINUE'},type ${gestureInfo.type}`
return this.rejectPan ? GestureJudgeResult.REJECT : GestureJudgeResult.CONTINUE
})
.onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, recognizers: Array<GestureRecognizer>): GestureJudgeResult => {
this.recognizerText = `onGestureRecognizerJudgeBegin:识别器数量 ${recognizers.length}`
if (this.rejectPan && current.getTag() === 'conflict-pan') {
current.preventBegin()
this.recognizerText = 'onGestureRecognizerJudgeBegin:conflict-pan 已阻止 begin'
return GestureJudgeResult.REJECT
}
return GestureJudgeResult.CONTINUE
})
.shouldBuiltInRecognizerParallelWith((current: GestureRecognizer, others: Array<GestureRecognizer>): GestureRecognizer => {
this.parallelText = `shouldBuiltInRecognizerParallelWith:候选数量 ${others.length}`
return current
})
Column({ space: 8 }) {
Text(this.judgeText)
.fontSize(14)
.fontColor('#111827')
.lineHeight(22)
Text(this.recognizerText)
.fontSize(14)
.fontColor('#111827')
.lineHeight(22)
Text(this.parallelText)
.fontSize(14)
.fontColor('#111827')
.lineHeight(22)
}
.width('100%')
.padding(16)
.borderRadius(14)
.backgroundColor('#FFFFFF')
.border({ width: 1, color: '#E5E7EB' })
}
.width('100%')
.padding(24)
.alignItems(HorizontalAlign.Start)
}
.width('100%')
.height('100%')
.backgroundColor('#F8FAFC')
}
}
【版权声明】本文为华为云社区用户原创内容,未经允许不得转载,如需转载请自行联系原作者进行授权。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱:
cloudbbs@huaweicloud.com
- 点赞
- 收藏
- 关注作者
评论(0)