1# 手势拦截增强 2 3为组件提供手势拦截能力。开发者可根据需要,将系统内置手势和比其优先级高的手势做并行化处理,并可以动态控制手势事件的触发。 4 5> **说明:** 6> 7> 从API Version 12开始支持。后续版本如有新增内容,则采用上角标单独标记该内容的起始版本。 8 9## shouldBuiltInRecognizerParallelWith 10 11shouldBuiltInRecognizerParallelWith(callback: ShouldBuiltInRecognizerParallelWithCallback): T 12 13提供系统内置手势与响应链上其他组件的手势设置并行关系的回调事件。 14 15**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 16 17**系统能力:** SystemCapability.ArkUI.ArkUI.Full 18 19**参数:** 20| 参数名 | 参数类型 | 必填 | 参数描述 | 21| ---------- | -------------------------- | ------- | ----------------------------- | 22| callback | [ShouldBuiltInRecognizerParallelWithCallback](#shouldbuiltinrecognizerparallelwithcallback) | 是 | 提供系统内置手势与响应链上其他组件的手势设置并行关系的回调事件,当该组件做触摸碰撞测试时,会触发用户定义的回调来形成手势并行关系。 | 23 24**返回值:** 25 26| 类型 | 说明 | 27| -------- | -------- | 28| T | 返回当前组件。 | 29 30## ShouldBuiltInRecognizerParallelWithCallback 31 32type ShouldBuiltInRecognizerParallelWithCallback = (current: GestureRecognizer, others: Array\<GestureRecognizer\>) => GestureRecognizer 33 34提供系统内置手势与响应链上其他组件的手势设置并行关系的回调事件类型。 35 36**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 37 38**系统能力:** SystemCapability.ArkUI.ArkUI.Full 39 40**参数:** 41 42| 参数名 | 类型 | 必填 | 说明 | 43| -------- | ------------------------- | ---- | ------------------------------------------------------------ | 44| current | [GestureRecognizer](#gesturerecognizer) | 是 | 当前组件的系统内置手势识别器,当前版本只提供内置的[PAN_GESTURE](ts-gesture-customize-judge.md#gesturejudgeresult11)类型的手势识别器。 | 45| others | Array\<[GestureRecognizer](#gesturerecognizer)\> | 是 | 响应链上更高优先级的其他组件相同类别的手势识别器。 | 46 47**返回值:** 48 49| 类型 | 说明 | 50| ------ | --------- | 51| [GestureRecognizer](#gesturerecognizer) | 与current识别器绑定并行关系的某个手势识别器。 | 52 53## GestureRecognizer 54 55手势识别器对象。 56 57### getTag 58 59getTag(): string 60 61返回当前手势识别器的tag。 62 63**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 64 65**系统能力:** SystemCapability.ArkUI.ArkUI.Full 66 67**返回值:** 68 69| 类型 | 说明 | 70| ------ | --------- | 71| string | 当前手势识别器的tag。 | 72 73### getType 74 75getType(): GestureControl.GestureType 76 77返回当前手势识别器的类型。 78 79**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 80 81**系统能力:** SystemCapability.ArkUI.ArkUI.Full 82 83**返回值:** 84 85| 类型 | 说明 | 86| ------ | --------- | 87| [GestureControl.GestureType](ts-gesture-customize-judge.md#gesturetype11) | 当前手势识别器的类型。 | 88 89### isBuiltIn 90 91isBuiltIn(): boolean 92 93返回当前手势识别器是否为系统内置手势。 94 95**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 96 97**系统能力:** SystemCapability.ArkUI.ArkUI.Full 98 99**返回值:** 100 101| 类型 | 说明 | 102| ------ | --------- | 103| boolean | 当前手势识别器是否为系统内置手势。 | 104 105### setEnabled 106 107setEnabled(isEnabled: boolean): void 108 109设置当前手势识别器的使能状态。 110 111**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 112 113**系统能力:** SystemCapability.ArkUI.ArkUI.Full 114 115**参数:** 116 117| 参数名 | 类型 | 必填 | 说明 | 118| ------- | ---------------------------------- | ---- | ----- | 119| isEnabled | boolean | 是 | 手势识别器的使能状态。 | 120 121### isEnabled 122 123isEnabled(): boolean 124 125返回当前手势识别器的使能状态。 126 127**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 128 129**系统能力:** SystemCapability.ArkUI.ArkUI.Full 130 131**返回值:** 132 133| 类型 | 说明 | 134| ------ | --------- | 135| boolean | 当前手势识别器的使能状态。 | 136 137### getState 138 139getState(): GestureRecognizerState 140 141返回当前手势识别器的状态。 142 143**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 144 145**系统能力:** SystemCapability.ArkUI.ArkUI.Full 146 147**返回值:** 148 149| 类型 | 说明 | 150| ------ | --------- | 151| [GestureRecognizerState](#gesturerecognizerstate) | 当前手势识别器的状态。 | 152 153### getEventTargetInfo 154 155getEventTargetInfo(): EventTargetInfo 156 157返回当前手势识别器对应组件的信息。 158 159**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 160 161**系统能力:** SystemCapability.ArkUI.ArkUI.Full 162 163**返回值:** 164 165| 类型 | 说明 | 166| ------ | --------- | 167| [EventTargetInfo](#eventtargetinfo) | 当前手势识别器对应组件的信息。 | 168 169### isValid 170 171isValid(): boolean; 172 173返回当前手势识别器是否有效。 174 175**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 176 177**系统能力:** SystemCapability.ArkUI.ArkUI.Full 178 179**返回值:** 180 181| 类型 | 说明 | 182| ------ | --------- | 183| boolean | 当前手势识别器是否有效。当该识别器绑定的组件被析构或者该识别器不在响应链上时返回false。 | 184 185## GestureRecognizerState 186 187定义手势识别器状态。 188 189**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 190 191**系统能力:** SystemCapability.ArkUI.ArkUI.Full 192 193| 名称 | 值 | 描述 | 194| ------- | ---- | ---------------------------------- | 195| READY | 0 | 准备状态。 | 196| DETECTING | 1 | 检测状态。 | 197| PENDING | 2 | 等待状态。 | 198| BLOCKED | 3 | 阻塞状态。 | 199| SUCCESSFUL | 4 | 成功状态。 | 200| FAILED | 5 | 失败状态。 | 201 202## EventTargetInfo 203 204手势识别器对应组件的信息。 205 206### getId 207 208getId(): string 209 210返回当前组件的组件标识。 211 212**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 213 214**系统能力:** SystemCapability.ArkUI.ArkUI.Full 215 216**返回值:** 217 218| 类型 | 说明 | 219| ------ | --------- | 220| string | 当前组件的[组件标识](./ts-universal-attributes-component-id.md#id)。 | 221 222## ScrollableTargetInfo 223 224手势识别器对应的滚动类容器组件的信息,继承于[EventTargetInfo](#eventtargetinfo)。 225 226### isBegin 227 228isBegin(): boolean 229 230返回当前滚动类容器组件是否在顶部,如果为Swiper组件且在循环模式下返回false。 231 232**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 233 234**系统能力:** SystemCapability.ArkUI.ArkUI.Full 235 236**返回值:** 237 238| 类型 | 说明 | 239| ------ | --------- | 240| boolean | 当前滚动类容器组件是否在顶部。 | 241 242### isEnd 243 244isEnd(): boolean 245 246返回当前滚动类容器组件是否在底部,如果为Swiper组件且在循环模式下返回false。 247 248**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 249 250**系统能力:** SystemCapability.ArkUI.ArkUI.Full 251 252**返回值:** 253 254| 类型 | 说明 | 255| ------ | --------- | 256| boolean | 当前滚动类容器组件是否在底部。 | 257 258## PanRecognizer 259 260拖动手势识别器对象,继承于[GestureRecognizer](#gesturerecognizer)。 261 262### getPanGestureOptions 263 264getPanGestureOptions(): PanGestureOptions 265 266返回当前拖动手势识别器的属性。 267 268**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 269 270**系统能力:** SystemCapability.ArkUI.ArkUI.Full 271 272**返回值:** 273 274| 类型 | 说明 | 275| ------ | --------- | 276| [PanGestureOptions](./ts-basic-gestures-pangesture.md#pangestureoptions) | 当前拖动手势识别器的属性。 | 277 278## onGestureRecognizerJudgeBegin<sup>13+</sup> 279 280onGestureRecognizerJudgeBegin(callback: GestureRecognizerJudgeBeginCallback, exposeInnerGesture: boolean): T 281 282给组件绑定自定义手势识别器判定回调。 283 284新增exposeInnerGesture参数作为是否将回调暴露给ArkUI原生组合组件的内置组件的标识,当该标识置为true时,将回调暴露给ArkUI原生组合组件的内置组件。<br> 285对于不需要将回调暴露给ArkUI原生组合组件内置组件的场景,建议采用原有[onGestureRecognizerJudgeBegin](#ongesturerecognizerjudgebegin)接口。若要求将回调暴露给ArkUI原生组合组件的内置组件,建议使用该接口并将exposeInnerGesture设置为true。 286 287**原子化服务API:** 从API version 13开始,该接口支持在原子化服务中使用。 288 289**系统能力:** SystemCapability.ArkUI.ArkUI.Full 290 291**参数:** 292| 参数名 | 参数类型 | 必填 | 参数描述 | 293| ---------- | -------------------------- | ------- | ----------------------------- | 294| callback | [GestureRecognizerJudgeBeginCallback](#gesturerecognizerjudgebegincallback) | 是 | 给组件绑定自定义手势识别器判定回调,当绑定到该组件的手势被接受时,会触发用户定义的回调来获取结果。 | 295| exposeInnerGesture | boolean | 是 | 暴露内部手势标识。<br/>默认值:false<br/>**说明:**<br/>如果是组合组件,此参数设置true,则会在current参数回调出组合组件内部的手势识别器。<br>当前仅支持[Tabs](ts-container-tabs.md),其他组件请不要设置此参数。<br/>设置为false时,功能与原接口[onGestureRecognizerJudgeBegin](#ongesturerecognizerjudgebegin)相同。 | 296 297## onGestureRecognizerJudgeBegin 298 299onGestureRecognizerJudgeBegin(callback: GestureRecognizerJudgeBeginCallback): T 300 301给组件绑定自定义手势识别器判定回调。 302 303**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 304 305**系统能力:** SystemCapability.ArkUI.ArkUI.Full 306 307**参数:** 308| 参数名 | 参数类型 | 必填 | 参数描述 | 309| ---------- | -------------------------- | ------- | ----------------------------- | 310| callback | [GestureRecognizerJudgeBeginCallback](#gesturerecognizerjudgebegincallback) | 是 | 给组件绑定自定义手势识别器判定回调,当绑定到该组件的手势被接受时,会触发用户定义的回调来获取结果。 | 311 312**返回值:** 313 314| 类型 | 说明 | 315| -------- | -------- | 316| T | 返回当前组件。 | 317 318## GestureRecognizerJudgeBeginCallback 319 320type GestureRecognizerJudgeBeginCallback = (event: BaseGestureEvent, current: GestureRecognizer, recognizers: Array\<GestureRecognizer\>) => GestureJudgeResult 321 322自定义手势识别器判定回调类型。 323 324**原子化服务API:** 从API version 12开始,该接口支持在原子化服务中使用。 325 326**系统能力:** SystemCapability.ArkUI.ArkUI.Full 327 328**参数:** 329 330| 参数名 | 类型 | 必填 | 说明 | 331| -------- | ------------------------- | ---- | ------------------------------------------------------------ | 332| event | [BaseGestureEvent](./ts-gesture-customize-judge.md#basegestureevent对象说明) | 是 | 当前基础手势事件信息。 | 333| current | [GestureRecognizer](#gesturerecognizer) | 是 | 当前即将要响应的识别器对象。 | 334| others | Array\<[GestureRecognizer](#gesturerecognizer)\> | 是 | 响应链上的其他手势识别器对象。 | 335 336**返回值:** 337 338| 类型 | 说明 | 339| ------ | --------- | 340| [GestureJudgeResult](ts-gesture-customize-judge.md#gesturejudgeresult11) | 手势是否裁决成功的判定结果。 | 341 342## 示例 343 344### 示例1 345 346```ts 347// xxx.ets 348@Entry 349@Component 350struct FatherControlChild { 351 scroller: Scroller = new Scroller() 352 scroller2: Scroller = new Scroller() 353 private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 354 private childRecognizer: GestureRecognizer = new GestureRecognizer() 355 private currentRecognizer: GestureRecognizer = new GestureRecognizer() 356 private lastOffset: number = 0 357 358 build() { 359 Stack({ alignContent: Alignment.TopStart }) { 360 Scroll(this.scroller) { // 外部滚动容器 361 Column() { 362 Text("Scroll Area") 363 .width('90%') 364 .height(150) 365 .backgroundColor(0xFFFFFF) 366 .borderRadius(15) 367 .fontSize(16) 368 .textAlign(TextAlign.Center) 369 .margin({ top: 10 }) 370 Scroll(this.scroller2) { // 内部滚动容器 371 Column() { 372 Text("Scroll Area2") 373 .width('90%') 374 .height(150) 375 .backgroundColor(0xFFFFFF) 376 .borderRadius(15) 377 .fontSize(16) 378 .textAlign(TextAlign.Center) 379 .margin({ top: 10 }) 380 Column() { 381 ForEach(this.arr, (item: number) => { 382 Text(item.toString()) 383 .width('90%') 384 .height(150) 385 .backgroundColor(0xFFFFFF) 386 .borderRadius(15) 387 .fontSize(16) 388 .textAlign(TextAlign.Center) 389 .margin({ top: 10 }) 390 }, (item: string) => item) 391 }.width('100%') 392 } 393 } 394 .id("inner") 395 .width('100%') 396 .height(800) 397 }.width('100%') 398 } 399 .id("outer") 400 .height(600) 401 .scrollable(ScrollDirection.Vertical) // 滚动方向纵向 402 .scrollBar(BarState.On) // 滚动条常驻显示 403 .scrollBarColor(Color.Gray) // 滚动条颜色 404 .scrollBarWidth(10) // 滚动条宽度 405 .edgeEffect(EdgeEffect.None) 406 .shouldBuiltInRecognizerParallelWith((current: GestureRecognizer, others: Array<GestureRecognizer>) => { 407 for (let i = 0; i < others.length; i++) { 408 let target = others[i].getEventTargetInfo(); 409 if (target) { 410 if (target.getId() == "inner" && others[i].isBuiltIn() && others[i].getType() == GestureControl.GestureType.PAN_GESTURE) { // 找到将要组成并行手势的识别器 411 this.currentRecognizer = current; // 保存当前组件的识别器 412 this.childRecognizer = others[i]; // 保存将要组成并行手势的识别器 413 return others[i]; // 返回将要组成并行手势的识别器 414 } 415 } 416 } 417 return undefined; 418 }) 419 .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, others: Array<GestureRecognizer>) => { // 在识别器即将要成功时,根据当前组件状态,设置识别器使能状态 420 if (current) { 421 let target = current.getEventTargetInfo(); 422 if (target) { 423 if (target.getId() == "outer" && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) { 424 if (others) { 425 for (let i = 0; i < others.length; i++) { 426 let target = others[i].getEventTargetInfo() as ScrollableTargetInfo; 427 if (target instanceof ScrollableTargetInfo && target.getId() == "inner") { // 找到响应链上对应并行的识别器 428 let panEvent = event as PanGestureEvent; 429 if (target.isEnd()) { // 根据当前组件状态以及移动方向动态控制识别器使能状态 430 if (panEvent && panEvent.offsetY < 0) { 431 this.childRecognizer.setEnabled(false) 432 this.currentRecognizer.setEnabled(true) 433 } else { 434 this.childRecognizer.setEnabled(true) 435 this.currentRecognizer.setEnabled(false) 436 } 437 } else if (target.isBegin()) { 438 if (panEvent.offsetY > 0) { 439 this.childRecognizer.setEnabled(false) 440 this.currentRecognizer.setEnabled(true) 441 } else { 442 this.childRecognizer.setEnabled(true) 443 this.currentRecognizer.setEnabled(false) 444 } 445 } else { 446 this.childRecognizer.setEnabled(true) 447 this.currentRecognizer.setEnabled(false) 448 } 449 } 450 } 451 } 452 } 453 } 454 } 455 return GestureJudgeResult.CONTINUE; 456 }) 457 .parallelGesture( // 绑定一个Pan手势作为动态控制器 458 PanGesture() 459 .onActionUpdate((event: GestureEvent)=>{ 460 if (this.childRecognizer.getState() != GestureRecognizerState.SUCCESSFUL || this.currentRecognizer.getState() != GestureRecognizerState.SUCCESSFUL) { // 如果识别器状态不是SUCCESSFUL,则不做控制 461 return; 462 } 463 let target = this.childRecognizer.getEventTargetInfo() as ScrollableTargetInfo; 464 let currentTarget = this.currentRecognizer.getEventTargetInfo() as ScrollableTargetInfo; 465 if (target instanceof ScrollableTargetInfo && currentTarget instanceof ScrollableTargetInfo) { 466 if (target.isEnd()) { // 在移动过程中实时根据当前组件状态,控制识别器的开闭状态 467 if ((event.offsetY - this.lastOffset) < 0) { 468 this.childRecognizer.setEnabled(false) 469 if (currentTarget.isEnd()) { 470 this.currentRecognizer.setEnabled(false) 471 } else { 472 this.currentRecognizer.setEnabled(true) 473 } 474 } else { 475 this.childRecognizer.setEnabled(true) 476 this.currentRecognizer.setEnabled(false) 477 } 478 } else if (target.isBegin()) { 479 if ((event.offsetY - this.lastOffset) > 0) { 480 this.childRecognizer.setEnabled(false) 481 if (currentTarget.isBegin()) { 482 this.currentRecognizer.setEnabled(false) 483 } else { 484 this.currentRecognizer.setEnabled(true) 485 } 486 } else { 487 this.childRecognizer.setEnabled(true) 488 this.currentRecognizer.setEnabled(false) 489 } 490 } else { 491 this.childRecognizer.setEnabled(true) 492 this.currentRecognizer.setEnabled(false) 493 } 494 } 495 this.lastOffset = event.offsetY 496 }) 497 ) 498 }.width('100%').height('100%').backgroundColor(0xDCDCDC) 499 } 500} 501``` 502 503### 示例2 504 505本示例通过将参数exposeInnerGesture设置为true,实现了一级Tabs容器在嵌套二级Tabs的场景下,能够屏蔽二级Tabs内置Swiper的滑动手势,从而触发一级Tabs内置Swiper滑动手势的功能。 506 507```ts 508// xxx.ets 509@Entry 510@Component 511struct Index { 512 @State currentIndex: number = 0 513 @State selectedIndex: number = 0 514 @State fontColor: string = '#182431' 515 @State selectedFontColor: string = '#007DFF' 516 controller?: TabsController = new TabsController(); 517 @Builder 518 tabBuilder(index: number, name: string) { 519 Column() { 520 Text(name) 521 .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor) 522 .fontSize(16) 523 .fontWeight(this.selectedIndex === index ? 500 : 400) 524 .lineHeight(22) 525 .margin({ top: 17, bottom: 7 }) 526 Divider() 527 .strokeWidth(2) 528 .color('#007DFF') 529 .opacity(this.selectedIndex === index ? 1 : 0) 530 }.width('100%') 531 } 532 build() { 533 Column() { 534 Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) { 535 TabContent() { 536 Column().width('100%').height('100%').backgroundColor(Color.Green) 537 }.tabBar(this.tabBuilder(0, 'green')) 538 TabContent() { 539 Tabs() { 540 TabContent() { 541 Column().width('100%').height('100%').backgroundColor(Color.Blue) 542 }.tabBar(new SubTabBarStyle('blue')) 543 TabContent() { 544 Column().width('100%').height('100%').backgroundColor(Color.Pink) 545 }.tabBar(new SubTabBarStyle('pink')) 546 } 547 .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, 548 others: Array<GestureRecognizer>): GestureJudgeResult => { // 在识别器即将要成功时,根据当前组件状态,设置识别器使能状态 549 console.info('ets onGestureRecognizerJudgeBegin child') 550 if (current) { 551 let target = current.getEventTargetInfo(); 552 if (target && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) { 553 console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE') 554 let swiperTaget = target as ScrollableTargetInfo 555 if (swiperTaget instanceof ScrollableTargetInfo) { 556 console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE isEnd: ' + swiperTaget.isEnd() + ' isBegin: ' + swiperTaget.isBegin()) 557 } 558 if (swiperTaget instanceof ScrollableTargetInfo && (swiperTaget.isEnd() || swiperTaget.isBegin())) { 559 let panEvent = event as PanGestureEvent; 560 console.log('pan direction:' + panEvent.offsetX + ' begin:' + swiperTaget.isBegin() + ' end:' + 561 swiperTaget.isEnd()) 562 if (panEvent && panEvent.offsetX < 0 && swiperTaget.isEnd()) { 563 console.info('ets onGestureRecognizerJudgeBegin child reject end') 564 return GestureJudgeResult.REJECT; 565 } 566 if (panEvent && panEvent.offsetX > 0 && swiperTaget.isBegin()) { 567 console.info('ets onGestureRecognizerJudgeBegin child reject begin') 568 return GestureJudgeResult.REJECT; 569 } 570 } 571 } 572 } 573 return GestureJudgeResult.CONTINUE; 574 }, true) 575 }.tabBar(this.tabBuilder(1, 'blue and pink')) 576 TabContent() { 577 Column().width('100%').height('100%').backgroundColor(Color.Brown) 578 }.tabBar(this.tabBuilder(2, 'brown')) 579 } 580 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 581 // 切换动画开始时触发该回调。目标页签显示下划线。 582 this.selectedIndex = targetIndex 583 }) 584 } 585 } 586} 587``` 588 589 