HarmonyOS 鸿蒙Next中如何正确使用Tabs嵌套
HarmonyOS 鸿蒙Next中如何正确使用Tabs嵌套
鸿蒙中如何正确使用Tabs嵌套
问题描述:
开发中经常会用到tabs嵌套tabs的情况,如果我们直接使用tabs嵌套tabs,会发现在内部tabs滑动到第一个和最后一个时,如果再次滑动,外部tabs不会响应事件,不会跟随滑动。
问题解决方法
使用最新api13提供的手势拦截增强功能,
注意:一定是接受两个参数,第二个参数设置为true,
如何使用api13新增方法
如果你按照上面官网把代码复制到自己的编译器大概率是会报错,因为自己本地编译器不支持api13,此时编译器版本为构建版本:5.0.3.806,构建 2024年9月11日,
onGestureRecognizerJudgeBegin 方法只接受一个参数,
如果不支持大家可以打开自己的编译器选择DevEco Studio菜单,选择Preferences(首选项),点击OpenHarmony SDK,查看自己的编译器是否可以下载API Version13
如果你的编译器也不支持,请执行以下步骤
第一步
请下载最新版本编译器, 点击访问最新下载地址
下载完最新版编译器之后你再执行上面操作发现有了API Version13的版本
选中点击确定会自动取下载,下载完之后代码就不会再报错,
第二步
更新项目"compatibleSdkVersion": “5.0.1(13)”,位置位于根目录下build-profile.json5文件
注意:如果你是模拟器这个时候有可能模拟器版本过低导致高版本app无法再低版本模拟器上运行
如何使用 onGestureRecognizerJudgeBegin
接受两个参数第一个是一个回调函数,第二个是boolean类型标示是否暴漏内部手势,这里传递true,
GestureRecognizerJudgeBeginCallback 可以获取手势事件和手势事件可传递的控件,这个方法是有返回值的,简单理解
return GestureJudgeResult.CONTINUE; 标示事件又自己处理,
return GestureJudgeResult.REJECT; 事件交给上层去处理
下面我们看一下官方代码如何实现吧:
// xxx.ets
@Entry
@Component
struct Index {
  @State currentIndex: number = 0
  @State selectedIndex: number = 0
  @State fontColor: string = '#182431'
  @State selectedFontColor: string = '#007DFF'
  innerSelectedIndex: number = 0 // 记录内层Tabs的索引
  controller?: TabsController = new TabsController();
  @Builder
  tabBuilder(index: number, name: string) {
    Column() {
      Text(name)
        .fontColor(this.selectedIndex === index ? this.selectedFontColor : this.fontColor)
        .fontSize(16)
        .fontWeight(this.selectedIndex === index ? 500 : 400)
        .lineHeight(22)
        .margin({ top: 17, bottom: 7 })
      Divider()
        .strokeWidth(2)
        .color('#007DFF')
        .opacity(this.selectedIndex === index ? 1 : 0)
    }.width('100%')
  }
  build() {
    Column() {
      Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
        TabContent() {
          Column().width('100%').height('100%').backgroundColor(Color.Green)
        }.tabBar(this.tabBuilder(0, 'green'))
        TabContent() {
          Tabs() {
            TabContent() {
              Column().width('100%').height('100%').backgroundColor(Color.Blue)
            }.tabBar(new SubTabBarStyle('blue'))
            TabContent() {
              Column().width('100%').height('100%').backgroundColor(Color.Pink)
            }.tabBar(new SubTabBarStyle('pink'))
          }
          .onAnimationStart((index: number, targetIndex: number) => {
            console.info('ets onGestureRecognizerJudgeBegin child:' + targetIndex)
            this.innerSelectedIndex = targetIndex
          })
          .onGestureRecognizerJudgeBegin((event: BaseGestureEvent, current: GestureRecognizer,
            others: Array<GestureRecognizer>): GestureJudgeResult => { // 在识别器即将要成功时,根据当前组件状态,设置识别器使能状态
            console.info('ets onGestureRecognizerJudgeBegin child')
            if (current) {
              let target = current.getEventTargetInfo();
              if (target && current.isBuiltIn() && current.getType() == GestureControl.GestureType.PAN_GESTURE) {
                console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE')
                let swiperTaget = target as ScrollableTargetInfo
                if (swiperTaget instanceof ScrollableTargetInfo) {
                  console.info('ets onGestureRecognizerJudgeBegin child PAN_GESTURE isEnd: ' + swiperTaget.isEnd() + ' isBegin: ' + swiperTaget.isBegin())
                }
                if (swiperTaget instanceof ScrollableTargetInfo &&
                    ((swiperTaget.isEnd() || this.innerSelectedIndex === 1) || // 此处判断swiperTaget.isEnd()或innerSelectedIndex === 内层Tabs的总数 - 1,表明内层Tabs滑动到尽头
                      (swiperTaget.isBegin() || this.innerSelectedIndex === 0))) { // 此处判断swiperTaget.isBegin()或innerSelectedIndex === 0,表明内层Tabs滑动到开头
                  let panEvent = event as PanGestureEvent;
                  console.log('pan direction:' + panEvent.offsetX + ' begin:' + swiperTaget.isBegin() + ' end:' +
                              swiperTaget.isEnd() + ' index:' + this.innerSelectedIndex)
                  if (panEvent && panEvent.offsetX < 0 && (swiperTaget.isEnd() || this.innerSelectedIndex === 1)) {
                    console.info('ets onGestureRecognizerJudgeBegin child reject end')
                    return GestureJudgeResult.REJECT;
                  }
                  if (panEvent && panEvent.offsetX > 0 && (swiperTaget.isBegin() || this.innerSelectedIndex === 0)) {
                    console.info('ets onGestureRecognizerJudgeBegin child reject begin')
                    return GestureJudgeResult.REJECT;
                  }
                }
              }
            }
            return GestureJudgeResult.CONTINUE;
          }, true)
        }.tabBar(this.tabBuilder(1, 'blue and pink'))
        TabContent() {
          Column().width('100%').height('100%').backgroundColor(Color.Brown)
        }.tabBar(this.tabBuilder(2, 'brown'))
      }
      .onAnimationStart((index: number, targetIndex: number, event: TabsAnimationEvent) => {
        // 切换动画开始时触发该回调。目标页签显示下划线。
        this.selectedIndex = targetIndex
      })
    }
  }
}
以上是最简单的双tabs嵌套实现。
如果内部 tabs 显示的是一个 web,此时 web 也需要使用 onGestureRecognizerJudgeBegin 来做手势判断。
更多关于HarmonyOS 鸿蒙Next中如何正确使用Tabs嵌套的实战教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中使用Tabs嵌套,需遵循以下步骤:
- 外层Tabs使用
TabContent组件布局 - 内层Tabs作为外层TabContent的子组件
 - 每个TabContent需设置对应高度
 - 嵌套Tabs需分别设置controller控制切换
 
示例代码结构:
Tabs({ controller: outerController }) {
  TabContent() {
    Tabs({ controller: innerController }) {
      TabContent() { /* 内容 */ }
    }
  }
}
注意避免多层嵌套导致的性能问题,推荐不超过2层嵌套。
更多关于HarmonyOS 鸿蒙Next中如何正确使用Tabs嵌套的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS Next中实现Tabs嵌套的正确方式是通过API13提供的手势拦截增强功能。关键点如下:
- 
核心解决方案是使用
onGestureRecognizerJudgeBegin方法,该方法需要:- 接受两个参数(回调函数和
boolean值) - 第二个参数必须设为
true以暴露内部手势 
 - 接受两个参数(回调函数和
 - 
实现逻辑:
- 当内部Tabs滑动到边界时(通过
isBegin()/isEnd()判断) - 根据滑动方向(
offsetX正负值)决定是否将事件传递给外层Tabs - 返回
GestureJudgeResult.REJECT让外层处理手势 
 - 当内部Tabs滑动到边界时(通过
 - 
开发环境要求:
- 必须使用支持API13的DevEco Studio版本
 - 在
build-profile.json5中设置"compatibleSdkVersion": "5.0.1(13)" 
 - 
注意事项:
- 模拟器也需要支持API13
 - Web组件嵌套时同样需要实现手势判断
 - 通过
innerSelectedIndex记录内部Tabs位置状态 
 
这种实现方式确保了内外层Tabs的手势滑动能正确联动,解决了边界滑动不响应的问题。
        
      
                  
                  
                  
