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 