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的手势滑动能正确联动,解决了边界滑动不响应的问题。