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该示例通过shouldBuiltInrecognizerParallelWith和onGestureRecognizerJudgeBegin实现了嵌套滚动的功能。内部组件优先响应滑动手势,当内部组件滑动至顶部或底部时,外部组件能够接替滑动。
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) { // 外部滚动容器
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) { // 内部滚动容器
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) // 滚动方向纵向
404      .scrollBar(BarState.On) // 滚动条常驻显示
405      .scrollBarColor(Color.Gray) // 滚动条颜色
406      .scrollBarWidth(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) { // 找到将要组成并行手势的识别器
413              this.currentRecognizer = current; // 保存当前组件的识别器
414              this.childRecognizer = others[i]; // 保存将要组成并行手势的识别器
415              return others[i]; // 返回将要组成并行手势的识别器
416            }
417          }
418        }
419        return undefined;
420      })
421      .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer, others: Array<GestureRecognizer>) => { // 在识别器即将要成功时,根据当前组件状态,设置识别器使能状态
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") { // 找到响应链上对应并行的识别器
430                    let panEvent = event as PanGestureEvent;
431                    if (target.isEnd()) { // 根据当前组件状态以及移动方向动态控制识别器使能状态
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( // 绑定一个Pan手势作为动态控制器
460        PanGesture()
461          .onActionUpdate((event: GestureEvent)=>{
462            if (this.childRecognizer.getState() != GestureRecognizerState.SUCCESSFUL || this.currentRecognizer.getState() != GestureRecognizerState.SUCCESSFUL) { // 如果识别器状态不是SUCCESSFUL,则不做控制
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()) { // 在移动过程中实时根据当前组件状态,控制识别器的开闭状态
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### 示例2(嵌套场景下拦截内部容器手势)
506
507本示例通过将参数exposeInnerGesture设置为true,实现了一级Tabs容器在嵌套二级Tabs的场景下,能够屏蔽二级Tabs内置Swiper的滑动手势,从而触发一级Tabs内置Swiper滑动手势的功能。
508开发者自行定义变量来记录内层Tabs的索引值,通过该索引值判断当滑动达到内层Tabs的边界处时,触发回调返回屏蔽使外层Tabs产生滑动手势。
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 // 记录内层Tabs的索引
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 => { // 在识别器即将要成功时,根据当前组件状态,设置识别器使能状态
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) || // 此处判断swiperTaget.isEnd()或innerSelectedIndex === 内层Tabs的总数 - 1,表明内层Tabs滑动到尽头
568                    (swiperTaget.isBegin() || this.innerSelectedIndex === 0))) { // 此处判断swiperTaget.isBegin()或innerSelectedIndex === 0,表明内层Tabs滑动到开头
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        // 切换动画开始时触发该回调。目标页签显示下划线。
592        this.selectedIndex = targetIndex
593      })
594    }
595  }
596}
597```
598
599 ![example](figures/gesture_recognizer.gif)