1# Gesture Blocking Enhancement 2 3Gesture blocking enhancement offers components the capability to block gestures. You can handle built-in gestures in parallel with gestures that have a higher priority as needed, and can dynamically control the triggering of gesture events. 4 5> **NOTE** 6> 7> The initial APIs of this module are supported since API version 12. Updates will be marked with a superscript to indicate their earliest API version. 8 9## shouldBuiltInRecognizerParallelWith 10 11shouldBuiltInRecognizerParallelWith(callback: ShouldBuiltInRecognizerParallelWithCallback): T 12 13Provides a callback to set the parallel relationship between built-in gestures and gestures of other components in the response chain. 14 15**Atomic service API**: This API can be used in atomic services since API version 12. 16 17**System capability**: SystemCapability.ArkUI.ArkUI.Full 18 19**Parameters** 20| Name | Type | Mandatory | Description | 21| ---------- | -------------------------- | ------- | ----------------------------- | 22| callback | [ShouldBuiltInRecognizerParallelWithCallback](#shouldbuiltinrecognizerparallelwithcallback) | Yes | Callback used to set the parallel relationship between built-in gestures and gestures of other components in the response chain. This callback is triggered during touch hit testing to form a gesture parallel relationship.| 23 24**Return value** 25 26| Type| Description| 27| -------- | -------- | 28| T | Current component.| 29 30## ShouldBuiltInRecognizerParallelWithCallback 31 32type ShouldBuiltInRecognizerParallelWithCallback = (current: GestureRecognizer, others: Array\<GestureRecognizer\>) => GestureRecognizer 33 34Represents the callback used to set the parallel relationship between built-in gestures and gestures of other components in the response chain. 35 36**Atomic service API**: This API can be used in atomic services since API version 12. 37 38**System capability**: SystemCapability.ArkUI.ArkUI.Full 39 40**Parameters** 41 42| Name | Type | Mandatory| Description | 43| -------- | ------------------------- | ---- | ------------------------------------------------------------ | 44| current | [GestureRecognizer](#gesturerecognizer) | Yes | Built-in gesture recognizer of the current component. Currently only a built-in gesture recognizer of the [PAN_GESTURE](ts-gesture-customize-judge.md#gesturejudgeresult11) type is supported.| 45| others | Array\<[GestureRecognizer](#gesturerecognizer)\> | Yes | Gesture recognizers of the same type from other components with higher priority in the response chain.| 46 47**Return value** 48 49| Type | Description | 50| ------ | --------- | 51| [GestureRecognizer](#gesturerecognizer) | Gesture recognizer that is bound in parallel with the current recognizer.| 52 53## GestureRecognizer 54 55Defines a gesture recognizer object. 56 57### getTag 58 59getTag(): string 60 61Obtains the tag of this gesture recognizer. 62 63**Atomic service API**: This API can be used in atomic services since API version 12. 64 65**System capability**: SystemCapability.ArkUI.ArkUI.Full 66 67**Return value** 68 69| Type | Description | 70| ------ | --------- | 71| string | Tag of the current gesture recognizer.| 72 73### getType 74 75getType(): GestureControl.GestureType 76 77Obtains the type of this gesture recognizer. 78 79**Atomic service API**: This API can be used in atomic services since API version 12. 80 81**System capability**: SystemCapability.ArkUI.ArkUI.Full 82 83**Return value** 84 85| Type | Description | 86| ------ | --------- | 87| [GestureControl.GestureType](ts-gesture-customize-judge.md#gesturetype11) | Type of the current gesture recognizer.| 88 89### isBuiltIn 90 91isBuiltIn(): boolean 92 93Obtains whether this gesture recognizer is a built-in gesture. 94 95**Atomic service API**: This API can be used in atomic services since API version 12. 96 97**System capability**: SystemCapability.ArkUI.ArkUI.Full 98 99**Return value** 100 101| Type | Description | 102| ------ | --------- | 103| boolean | Whether this gesture recognizer is a built-in gesture.| 104 105### setEnabled 106 107setEnabled(isEnabled: boolean): void 108 109Sets the enabled state of this gesture recognizer. 110 111**Atomic service API**: This API can be used in atomic services since API version 12. 112 113**System capability**: SystemCapability.ArkUI.ArkUI.Full 114 115**Parameters** 116 117| Name | Type | Mandatory | Description | 118| ------- | ---------------------------------- | ---- | ----- | 119| isEnabled | boolean | Yes | Enabled state to set.| 120 121### isEnabled 122 123isEnabled(): boolean 124 125Obtains the enabled state of this gesture recognizer. 126 127**Atomic service API**: This API can be used in atomic services since API version 12. 128 129**System capability**: SystemCapability.ArkUI.ArkUI.Full 130 131**Return value** 132 133| Type | Description | 134| ------ | --------- | 135| boolean | Enabled state of the gesture recognizer.| 136 137### getState 138 139getState(): GestureRecognizerState 140 141Obtains the state of this gesture recognizer. 142 143**Atomic service API**: This API can be used in atomic services since API version 12. 144 145**System capability**: SystemCapability.ArkUI.ArkUI.Full 146 147**Return value** 148 149| Type | Description | 150| ------ | --------- | 151| [GestureRecognizerState](#gesturerecognizerstate) | State of the gesture recognizer.| 152 153### getEventTargetInfo 154 155getEventTargetInfo(): EventTargetInfo 156 157Obtains the information about the component corresponding to this gesture recognizer. 158 159**Atomic service API**: This API can be used in atomic services since API version 12. 160 161**System capability**: SystemCapability.ArkUI.ArkUI.Full 162 163**Return value** 164 165| Type | Description | 166| ------ | --------- | 167| [EventTargetInfo](#eventtargetinfo) | Information about the component corresponding to the current gesture recognizer.| 168 169### isValid 170 171isValid(): boolean; 172 173Whether the current gesture recognizer is valid. 174 175**Atomic service API**: This API can be used in atomic services since API version 12. 176 177**System capability**: SystemCapability.ArkUI.ArkUI.Full 178 179**Return value** 180 181| Type | Description | 182| ------ | --------- | 183| boolean | Whether the current gesture recognizer is valid. Returns **false** if the component bound to this recognizer is destructed or if the recognizer is not on the response chain.| 184 185## GestureRecognizerState 186 187Enumerates the gesture recognizer states. 188 189**Atomic service API**: This API can be used in atomic services since API version 12. 190 191**System capability**: SystemCapability.ArkUI.ArkUI.Full 192 193| Name | Value | Description | 194| ------- | ---- | ---------------------------------- | 195| READY | 0 | Ready.| 196| DETECTING | 1 | Detecting.| 197| PENDING | 2 | Pending.| 198| BLOCKED | 3 | Blocked.| 199| SUCCESSFUL | 4 | Successful.| 200| FAILED | 5 | Failed.| 201 202## EventTargetInfo 203 204Provides the information about the component corresponding to the gesture recognizer. 205 206### getId 207 208getId(): string 209 210Obtains the ID of this component. 211 212**Atomic service API**: This API can be used in atomic services since API version 12. 213 214**System capability**: SystemCapability.ArkUI.ArkUI.Full 215 216**Return value** 217 218| Type | Description | 219| ------ | --------- | 220| string | [ID](./ts-universal-attributes-component-id.md#id) of the current component.| 221 222## ScrollableTargetInfo 223 224Provides the information about the scrollable container component corresponding to the gesture recognizer. It inherits from [EventTargetInfo](#eventtargetinfo). 225 226### isBegin 227 228isBegin(): boolean 229 230Checks whether this scrollable container component is scrolled to the top. If it is a **Swiper** component in loop mode, **false** is returned. 231 232**Atomic service API**: This API can be used in atomic services since API version 12. 233 234**System capability**: SystemCapability.ArkUI.ArkUI.Full 235 236**Return value** 237 238| Type | Description | 239| ------ | --------- | 240| boolean | Whether the current scrollable container component is scrolled to the top.| 241 242### isEnd 243 244isEnd(): boolean 245 246Checks whether this scrollable container component is scrolled to the bottom. If it is a **Swiper** component in loop mode, **false** is returned. 247 248**Atomic service API**: This API can be used in atomic services since API version 12. 249 250**System capability**: SystemCapability.ArkUI.ArkUI.Full 251 252**Return value** 253 254| Type | Description | 255| ------ | --------- | 256| boolean | Whether the current scrollable container component is scrolled to the bottom.| 257 258## PanRecognizer 259 260Defines a pan gesture recognizer object. It inherits from [GestureRecognizer](#gesturerecognizer). 261 262### getPanGestureOptions 263 264getPanGestureOptions(): PanGestureOptions 265 266Obtains the properties of this pan gesture recognizer. 267 268**Atomic service API**: This API can be used in atomic services since API version 12. 269 270**System capability**: SystemCapability.ArkUI.ArkUI.Full 271 272**Return value** 273 274| Type | Description | 275| ------ | --------- | 276| [PanGestureOptions](./ts-basic-gestures-pangesture.md#pangestureoptions) | Properties of the current pan gesture recognizer.| 277 278## onGestureRecognizerJudgeBegin<sup>13+</sup> 279 280onGestureRecognizerJudgeBegin(callback: GestureRecognizerJudgeBeginCallback, exposeInnerGesture: boolean): T 281 282Binds a custom gesture recognizer judgment callback to the component. 283 284The **exposeInnerGesture** parameter indicates whether to expose the callback to the built-in components of ArkUI native composite components.<br> 285For scenarios where the callback does not need to be exposed to the built-in components of ArkUI native composite components, you are advised to use the [onGestureRecognizerJudgeBegin](#ongesturerecognizerjudgebegin) API. If the callback needs to be exposed to the built-in components of ArkUI native composite components, use this API and set **exposeInnerGesture** to **true**. 286 287**Atomic service API**: This API can be used in atomic services since API version 13. 288 289**System capability**: SystemCapability.ArkUI.ArkUI.Full 290 291**Parameters** 292| Name | Type | Mandatory | Description | 293| ---------- | -------------------------- | ------- | ----------------------------- | 294| callback | [GestureRecognizerJudgeBeginCallback](#gesturerecognizerjudgebegincallback) | Yes | Custom gesture recognizer judgment callback to bind to the component. This callback is triggered when the gesture bound to the component is accepted to obtain the result.| 295| exposeInnerGesture | boolean | Yes | Whether to expose the internal gesture.<br>Default value: **false**<br>**NOTE**<br>For a composite component, setting this parameter to **true** exposes the internal gesture recognizer of the composite component in the **current** parameter callback.<br>Currently, only the [Tabs](ts-container-tabs.md) component is supported. Do not set this parameter for other components.<br>When this parameter is set to **false**, this API provides the same functionality as the [onGestureRecognizerJudgeBegin](#ongesturerecognizerjudgebegin) API.| 296 297## onGestureRecognizerJudgeBegin 298 299onGestureRecognizerJudgeBegin(callback: GestureRecognizerJudgeBeginCallback): T 300 301Binds a custom gesture recognizer judgment callback to the component. 302 303**Atomic service API**: This API can be used in atomic services since API version 12. 304 305**System capability**: SystemCapability.ArkUI.ArkUI.Full 306 307**Parameters** 308| Name | Type | Mandatory | Description | 309| ---------- | -------------------------- | ------- | ----------------------------- | 310| callback | [GestureRecognizerJudgeBeginCallback](#gesturerecognizerjudgebegincallback) | Yes | Custom gesture recognizer judgment callback to bind to the component. This callback is triggered when the gesture bound to the component is accepted to obtain the result.| 311 312**Return value** 313 314| Type| Description| 315| -------- | -------- | 316| T | Current component.| 317 318## GestureRecognizerJudgeBeginCallback 319 320type GestureRecognizerJudgeBeginCallback = (event: BaseGestureEvent, current: GestureRecognizer, recognizers: Array\<GestureRecognizer\>) => GestureJudgeResult 321 322Represents a custom gesture recognizer judgment callback. 323 324**Atomic service API**: This API can be used in atomic services since API version 12. 325 326**System capability**: SystemCapability.ArkUI.ArkUI.Full 327 328**Parameters** 329 330| Name | Type | Mandatory| Description | 331| -------- | ------------------------- | ---- | ------------------------------------------------------------ | 332| event | [BaseGestureEvent](./ts-gesture-customize-judge.md#basegestureevent) | Yes | Information about the current basic gesture event.| 333| current | [GestureRecognizer](#gesturerecognizer) | Yes | Gesture recognizer object that is about to respond.| 334| others | Array\<[GestureRecognizer](#gesturerecognizer)\> | Yes | Other gesture recognizer objects in the response chain.| 335 336**Return value** 337 338| Type | Description | 339| ------ | --------- | 340| [GestureJudgeResult](ts-gesture-customize-judge.md#gesturejudgeresult11) | Result of whether the gesture judgment is successful.| 341 342## Example 343 344### Example 1: Implementing Nested Scrolling 345 346This example demonstrates how to implement nested scrolling using **shouldBuiltInRecognizerParallelWith** and **onGestureRecognizerJudgeBegin**. The inner component takes precedence in responding to swipe gestures. When the inner component reaches the top or bottom, the outer component can then take over the scrolling. 347 348```ts 349// xxx.ets 350@Entry 351@Component 352struct FatherControlChild { 353 scroller: Scroller = new Scroller() 354 scroller2: Scroller = new Scroller() 355 private arr: number[] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 356 private childRecognizer: GestureRecognizer = new GestureRecognizer() 357 private currentRecognizer: GestureRecognizer = new GestureRecognizer() 358 private lastOffset: number = 0 359 360 build() { 361 Stack({ alignContent: Alignment.TopStart }) { 362 Scroll(this.scroller) { // Outer scrollable container. 363 Column() { 364 Text("Scroll Area") 365 .width('90%') 366 .height(150) 367 .backgroundColor(0xFFFFFF) 368 .borderRadius(15) 369 .fontSize(16) 370 .textAlign(TextAlign.Center) 371 .margin({ top: 10 }) 372 Scroll(this.scroller2) { // Inner scrollable container. 373 Column() { 374 Text("Scroll Area2") 375 .width('90%') 376 .height(150) 377 .backgroundColor(0xFFFFFF) 378 .borderRadius(15) 379 .fontSize(16) 380 .textAlign(TextAlign.Center) 381 .margin({ top: 10 }) 382 Column() { 383 ForEach(this.arr, (item: number) => { 384 Text(item.toString()) 385 .width('90%') 386 .height(150) 387 .backgroundColor(0xFFFFFF) 388 .borderRadius(15) 389 .fontSize(16) 390 .textAlign(TextAlign.Center) 391 .margin({ top: 10 }) 392 }, (item: string) => item) 393 }.width('100%') 394 } 395 } 396 .id("inner") 397 .width('100%') 398 .height(800) 399 }.width('100%') 400 } 401 .id("outer") 402 .height(600) 403 .scrollable(ScrollDirection.Vertical) // The scrollbar scrolls in the vertical direction. 404 .scrollBar(BarState.On) // The scrollbar is always displayed. 405 .scrollBarColor(Color.Gray) // The scrollbar color is gray. 406 .scrollBarWidth(10) // The scrollbar width is 10. 407 .edgeEffect(EdgeEffect.None) 408 .shouldBuiltInRecognizerParallelWith((current: GestureRecognizer, others: Array<GestureRecognizer>) => { 409 for (let i = 0; i < others.length; i++) { 410 let target = others[i].getEventTargetInfo(); 411 if (target) { 412 if (target.getId() == "inner" && others[i].isBuiltIn() && others[i].getType() == GestureControl.GestureType.PAN_GESTURE) { // Find the recognizer to form a parallel gesture. 413 this.currentRecognizer = current; // Save the recognizer of the current component. 414 this.childRecognizer = others[i]; // Save the recognizer to form a parallel gesture. 415 return others[i]; // Return the recognizer to form a parallel gesture. 416 } 417 } 418 } 419 return undefined; 420 }) 421 .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, others: Array<GestureRecognizer>) => { // When gesture recognition is about to be successful, set the recognizer's enabled state based on the current component state. 422 if (current) { 423 let target = current.getEventTargetInfo(); 424 if (target) { 425 if (target.getId() == "outer" && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) { 426 if (others) { 427 for (let i = 0; i < others.length; i++) { 428 let target = others[i].getEventTargetInfo() as ScrollableTargetInfo; 429 if (target instanceof ScrollableTargetInfo && target.getId() == "inner") { // Identify the recognizer to work in parallel on the response chain. 430 let panEvent = event as PanGestureEvent; 431 if (target.isEnd()) { // Dynamically control the recognizer's enabled state based on the current component state and direction of movement. 432 if (panEvent && panEvent.offsetY < 0) { 433 this.childRecognizer.setEnabled(false) 434 this.currentRecognizer.setEnabled(true) 435 } else { 436 this.childRecognizer.setEnabled(true) 437 this.currentRecognizer.setEnabled(false) 438 } 439 } else if (target.isBegin()) { 440 if (panEvent.offsetY > 0) { 441 this.childRecognizer.setEnabled(false) 442 this.currentRecognizer.setEnabled(true) 443 } else { 444 this.childRecognizer.setEnabled(true) 445 this.currentRecognizer.setEnabled(false) 446 } 447 } else { 448 this.childRecognizer.setEnabled(true) 449 this.currentRecognizer.setEnabled(false) 450 } 451 } 452 } 453 } 454 } 455 } 456 } 457 return GestureJudgeResult.CONTINUE; 458 }) 459 .parallelGesture( // Bind a pan gesture as a dynamic controller. 460 PanGesture() 461 .onActionUpdate((event: GestureEvent)=>{ 462 if (this.childRecognizer.getState() != GestureRecognizerState.SUCCESSFUL || this.currentRecognizer.getState() != GestureRecognizerState.SUCCESSFUL) { // If neither recognizer is in the SUCCESSFUL state, no control is applied. 463 return; 464 } 465 let target = this.childRecognizer.getEventTargetInfo() as ScrollableTargetInfo; 466 let currentTarget = this.currentRecognizer.getEventTargetInfo() as ScrollableTargetInfo; 467 if (target instanceof ScrollableTargetInfo && currentTarget instanceof ScrollableTargetInfo) { 468 if (target.isEnd()) { // Adjust the enabled state of the gesture recognizers based on the current component state during movement. 469 if ((event.offsetY - this.lastOffset) < 0) { 470 this.childRecognizer.setEnabled(false) 471 if (currentTarget.isEnd()) { 472 this.currentRecognizer.setEnabled(false) 473 } else { 474 this.currentRecognizer.setEnabled(true) 475 } 476 } else { 477 this.childRecognizer.setEnabled(true) 478 this.currentRecognizer.setEnabled(false) 479 } 480 } else if (target.isBegin()) { 481 if ((event.offsetY - this.lastOffset) > 0) { 482 this.childRecognizer.setEnabled(false) 483 if (currentTarget.isBegin()) { 484 this.currentRecognizer.setEnabled(false) 485 } else { 486 this.currentRecognizer.setEnabled(true) 487 } 488 } else { 489 this.childRecognizer.setEnabled(true) 490 this.currentRecognizer.setEnabled(false) 491 } 492 } else { 493 this.childRecognizer.setEnabled(true) 494 this.currentRecognizer.setEnabled(false) 495 } 496 } 497 this.lastOffset = event.offsetY 498 }) 499 ) 500 }.width('100%').height('100%').backgroundColor(0xDCDCDC) 501 } 502} 503``` 504 505### Example 2: Blocking Inner Container Gestures in Nested Scrolling 506 507This example demonstrates how to set the **exposeInnerGesture** parameter to **true** to enable a first-level **Tabs** container to intercept the swipe gestures of a nested second-level **Tabs** container, thereby triggering the swipe gestures of the built-in **Swiper** component of first-level **Tabs** container. 508You can define variables to record the index of the inner **Tabs** container and use this index to determine when to trigger the callback to block the swipe gestures of the outer **Tabs** container when the inner **Tabs** container reaches its boundaries. 509 510```ts 511// xxx.ets 512@Entry 513@Component 514struct Index { 515 @State currentIndex: number = 0 516 @State selectedIndex: number = 0 517 @State fontColor: string = '#182431' 518 @State selectedFontColor: string = '#007DFF' 519 innerSelectedIndex: number = 0 // Record the index of the inner Tabs container. 520 controller?: TabsController = new TabsController(); 521 @Builder 522 tabBuilder(index: number, name: string) { 523 Column() { 524 Text(name) 525 .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor) 526 .fontSize(16) 527 .fontWeight(this.selectedIndex === index ? 500 : 400) 528 .lineHeight(22) 529 .margin({ top: 17, bottom: 7 }) 530 Divider() 531 .strokeWidth(2) 532 .color('#007DFF') 533 .opacity(this.selectedIndex === index ? 1 : 0) 534 }.width('100%') 535 } 536 build() { 537 Column() { 538 Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) { 539 TabContent() { 540 Column().width('100%').height('100%').backgroundColor(Color.Green) 541 }.tabBar(this.tabBuilder(0, 'green')) 542 TabContent() { 543 Tabs() { 544 TabContent() { 545 Column().width('100%').height('100%').backgroundColor(Color.Blue) 546 }.tabBar(new SubTabBarStyle('blue')) 547 TabContent() { 548 Column().width('100%').height('100%').backgroundColor(Color.Pink) 549 }.tabBar(new SubTabBarStyle('pink')) 550 } 551 .onAnimationStart((index: number, targetIndex: number) => { 552 console.info('ets onGestureRecognizerJudgeBegin child:' + targetIndex) 553 this.innerSelectedIndex = targetIndex 554 }) 555 .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, 556 others: Array<GestureRecognizer>): GestureJudgeResult => { // When gesture recognition is about to be successful, set the recognizer's enabled state based on the current component state. 557 console.info('ets onGestureRecognizerJudgeBegin child') 558 if (current) { 559 let target = current.getEventTargetInfo(); 560 if (target && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) { 561 console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE') 562 let swiperTaget = target as ScrollableTargetInfo 563 if (swiperTaget instanceof ScrollableTargetInfo) { 564 console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE isEnd: ' + swiperTaget.isEnd() + ' isBegin: ' + swiperTaget.isBegin()) 565 } 566 if (swiperTaget instanceof ScrollableTargetInfo && 567 ((swiperTaget.isEnd() || this.innerSelectedIndex === 1) || // Check whether swiperTarget.isEnd() or innerSelectedIndex === total number of inner Tabs - 1, indicating the inner Tabs container has reached the end. 568 (swiperTaget.isBegin() || this.innerSelectedIndex === 0))) { // Check whether swiperTarget.isBegin() or innerSelectedIndex === 0, indicating the inner Tabs container has reached the beginning. 569 let panEvent = event as PanGestureEvent; 570 console.log('pan direction:' + panEvent.offsetX + ' begin:' + swiperTaget.isBegin() + ' end:' + 571 swiperTaget.isEnd() + ' index:' + this.innerSelectedIndex) 572 if (panEvent && panEvent.offsetX < 0 && (swiperTaget.isEnd() || this.innerSelectedIndex === 1)) { 573 console.info('ets onGestureRecognizerJudgeBegin child reject end') 574 return GestureJudgeResult.REJECT; 575 } 576 if (panEvent && panEvent.offsetX > 0 && (swiperTaget.isBegin() || this.innerSelectedIndex === 0)) { 577 console.info('ets onGestureRecognizerJudgeBegin child reject begin') 578 return GestureJudgeResult.REJECT; 579 } 580 } 581 } 582 } 583 return GestureJudgeResult.CONTINUE; 584 }, true) 585 }.tabBar(this.tabBuilder(1, 'blue and pink')) 586 TabContent() { 587 Column().width('100%').height('100%').backgroundColor(Color.Brown) 588 }.tabBar(this.tabBuilder(2, 'brown')) 589 } 590 .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => { 591 // Triggered when the switching animation starts. The target tab shows an underline. 592 this.selectedIndex = targetIndex 593 }) 594 } 595 } 596} 597``` 598 599  600