HarmonyOS 鸿蒙Next 两层Tabs嵌套滑动冲突问题
HarmonyOS 鸿蒙Next 两层Tabs嵌套滑动冲突问题
两层Tabs组件嵌套使用,内部的tabs组件可以左右滑动,但是在滑动到第一个或是最后一个时,外层Tabs无法响应滑动事件。
请教各位大佬,有什么方案可以实现内部Tabs滑动到页面边界时可以响应外层Tabs的滑动事件
楼主看下这个方案可行,通过PanGesture和controller.changeIndex实现一二级Tabs切换,当二级Tabs 滑动到第一个或者最后一个tab的时候 ,切换到一级Tabs 。
Demo如下:
@Entry
@Component
struct TabsExample {
@State fontColor: string = '#182431'
@State selectedFontColor: string = '#007DFF'
@State currentIndex: number = 0
private controller: TabsController = new TabsController()
@State subCurrentIndex: number = 0
private subController: TabsController = new TabsController()
private panOptionRight: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Left })
private panOptionLeft: PanGestureOptions = new PanGestureOptions({ direction: PanDirection.Right })
@Builder tabBuilder(index: number, name: string) {
Column() {
Text(name)
.fontColor(this.currentIndex === index ? this.selectedFontColor : this.fontColor)
.fontSize(16)
.fontWeight(this.currentIndex === index ? 500 : 400)
.lineHeight(22)
.margin({ top: 17, bottom: 7 })
Divider()
.strokeWidth(2)
.color('#007DFF')
.opacity(this.currentIndex === index ? 1 : 0)
}.width('100%')
}
@Builder subTabBuilder(index: number, name: string) {
Column() {
Text(name)
.fontColor(this.subCurrentIndex === index ? this.selectedFontColor : this.fontColor)
.fontSize(16)
.fontWeight(this.subCurrentIndex === index ? 500 : 400)
.lineHeight(22)
.margin({ top: 17, bottom: 7 })
Divider()
.strokeWidth(2)
.color('#007DFF')
.opacity(this.subCurrentIndex === index ? 1 : 0)
}.width('100%')
}
build() {
Column() {
Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
TabContent() {
Column().width('100%').height('100%').backgroundColor('#ffb554d7')
}.tabBar(this.tabBuilder(0, '首页'))
TabContent() {
Column(){
Tabs({ barPosition: BarPosition.Start,controller: this.subController }) {
TabContent() {
Column().width('100%').height('100%').backgroundColor('#00CB87')
// 左右拖动触发该手势事件
.gesture(
PanGesture(this.panOptionRight)
.onActionStart((event?: GestureEvent) => {
console.info('Pan start')
})
.onActionUpdate((event?: GestureEvent) => {
if (event) {
}
})
.onActionEnd(() => {
this.controller.changeIndex(2)
console.info('Pan end')
})
)
// .gesture(
// PanGesture(this.panOptionLeft)
// .onActionStart((event?: GestureEvent) => {
// console.info('Pan start')
// })
// .onActionUpdate((event?: GestureEvent) => {
// if (event) {
// }
// })
// .onActionEnd(() => {
// this.controller.changeIndex(0)
// console.info('Pan end')
// })
// )
}.tabBar(this.subTabBuilder(3, 'pink'))
}
.vertical(false)
.barMode(BarMode.Fixed)
.barWidth(360)
.barHeight(56)
.animationDuration(400)
.onChange((index: number) => {
this.subCurrentIndex = index
})
.width(360)
.backgroundColor('#F1F3F5')
}
.width('100%').height('100%').backgroundColor('#00CB87')
}.tabBar(this.tabBuilder(1, '详情'))
TabContent() {
Column().width('100%').height('100%').backgroundColor('#ffc19757')
}.tabBar(this.tabBuilder(2, '我的'))
}
.vertical(false)
.barMode(BarMode.Fixed)
.barWidth(360)
.barHeight(56)
.animationDuration(400)
.onChange((index: number) => {
this.currentIndex = index
})
.width(360)
.height(296)
.margin({ top: 52 })
.backgroundColor('#F1F3F5')
}.width('100%')
}
}
更多关于HarmonyOS 鸿蒙Next 两层Tabs嵌套滑动冲突问题的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
这个方案看到过,目前也采用了这种方案,虽然还有点问题。 希望官方能够把事件分发机制再优化下,如果能够像安卓那样从事件分发的角度解决这个问题要简单的多。
希望HarmonyOS能继续推出更多实用的功能,满足用户的不同需求。
这个解决方案看到过,但是外层Tabs切换时有些生硬,没有原始过度动画。不知道还有没有其他的解决方案。譬如,类似安卓中的事件分发,内部Tabs将事件抛给上层处理
最终采用了PanGesture和controller.changeIndex的实现方案,虽然滑动事件从内部Tabs向外层Tabs过度时动效上还有些问题,但已是目前较好的解决方案了。
不知道使用Tabs+Swiper 的组件嵌套方法会不会有意想不到的效果。
[@Entry](/user/Entry)
[@Component](/user/Component)
struct Index {
[@State](/user/State) currentIndex: number = 0
[@State](/user/State) selectedIndex: number = 0
[@State](/user/State) fontColor: string = '#182431'
[@State](/user/State) selectedFontColor: string = '#007DFF'
innerSelectedIndex: number = 0 // 记录内层Tabs的索引
controller?: TabsController = new TabsController();
[@Builder](/user/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
})
}
}
}
在HarmonyOS鸿蒙Next系统中,处理两层Tabs嵌套滑动冲突问题,通常涉及到对组件的滑动事件进行协调管理。以下是针对此问题的直接解决思路:
-
事件拦截:
- 在外层Tabs组件中,通过重写滑动事件处理方法,实现对滑动事件的拦截。当检测到滑动事件时,根据当前滑动状态判断是否需要将事件传递给内层Tabs组件。
-
滑动方向判断:
- 通过判断滑动手势的方向,如果滑动方向是横向(通常用于切换Tabs),则由外层Tabs处理;如果滑动方向是纵向(通常用于内容滚动),则允许事件传递给内层Tabs或其内容区域。
-
嵌套滚动视图:
- 使用鸿蒙提供的嵌套滚动视图组件(如
NestedScrollView
),该组件能够较好地处理嵌套滚动冲突,确保内外层Tabs的滑动行为互不干扰。
- 使用鸿蒙提供的嵌套滚动视图组件(如
-
滚动区域设置:
- 明确设置内外层Tabs的滚动区域,避免滚动区域重叠导致的冲突。
通过上述方法,可以有效解决HarmonyOS鸿蒙Next系统中两层Tabs嵌套时的滑动冲突问题。如果问题依旧没法解决请联系官网客服,官网地址是:https://www.itying.com/category-93-b0.html。