【鸿蒙 HarmonyOS NEXT】组件嵌套滚动:nestedScroll
✨本人自己开发的开源项目:土拨鼠充电系统
✨踩坑不易,还希望各位大佬支持一下,在GitHub给我点个 Start ⭐⭐👍👍
✍GitHub开源项目地址👉:https://github.com/cheinlu/groundhog-charging-system
一、背景
当滚动组件进行嵌套关系时,如果两个组件需要同时滚动可能会产生互斥效果,使用nestedScroll属性来解决嵌套组件的滚动问题
可滚动组件:Scroll、List、WaterFlow,这些组件中都有包含nestedScroll属性,用于解决组件嵌套的滚动联动
二、场景
tabs嵌套list组件,当tabs切换页签时,会与list组件的滚动出现互斥效果,期望按住文本或按钮区域都能实现切换页签效果
具体描述:
tabs下面展示文本信息与按钮,其中按钮是用list组件完成的,当按钮多的情况下是可以滚动按钮,目前问题是按住文本区域是可以左右切换tabs页签的,按住按钮区域切换tab就不行,怀疑是list滚动与tab页签切换互斥了
三、具体实现
3.1、示例代码
@Entry
@Component
struct TabsExample {
@State fontColor: string = '#182431'
@State selectedFontColor: string = '#007DFF'
@State currentIndex: number = 0
private controller: TabsController = new TabsController()
@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%')
}
build() {
Column() {
Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
TabContent() {
OrderList({ type: '1' })
}
.tabBar(this.tabBuilder(0, 'green'))
.gesture(PanGesture(new PanGestureOptions({ direction: PanDirection.Right })))
TabContent() {
OrderList({ type: '2' })
}.tabBar(this.tabBuilder(1, 'blue'))
TabContent() {
OrderList({ type: '3' })
}.tabBar(this.tabBuilder(2, 'yellow'))
TabContent() {
OrderList({ type: '4' })
}
.tabBar(this.tabBuilder(3, 'pink'))
.gesture(PanGesture(new PanGestureOptions({ direction: PanDirection.Left })))
}
.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%')
}
}
@Component
struct OrderList {
@Prop type: string
build() {
Column() {
Column({ space: 30 }) {
Row() {
Text('文本' + this.type)
}
Row() {
Text('文本' + this.type)
}
Row() {
Text('文本' + this.type)
}
}
.width('100%')
.height(150)
List() {
ListItem() {
Row() {
Text('取消订单' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('查看发票' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('申请发票' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('退换货' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('去支付' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
}
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.None)
.width('100%')
}
.width('100%')
}
}
3.2、实现效果
3.3、完成预期效果:按住按钮区域也能切换页签
解决方法:给list添加nestedScroll属性,解决嵌套组件的滚动问题
详细代码:
@Entry
@Component
struct TabsExample {
@State fontColor: string = '#182431'
@State selectedFontColor: string = '#007DFF'
@State currentIndex: number = 0
private controller: TabsController = new TabsController()
@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%')
}
build() {
Column() {
Tabs({ barPosition: BarPosition.Start, index: this.currentIndex, controller: this.controller }) {
TabContent() {
OrderList({ type: '1' })
}
.tabBar(this.tabBuilder(0, 'green'))
.gesture(PanGesture(new PanGestureOptions({ direction: PanDirection.Right })))
TabContent() {
OrderList({ type: '2' })
}.tabBar(this.tabBuilder(1, 'blue'))
TabContent() {
OrderList({ type: '3' })
}.tabBar(this.tabBuilder(2, 'yellow'))
TabContent() {
OrderList({ type: '4' })
}
.tabBar(this.tabBuilder(3, 'pink'))
.gesture(PanGesture(new PanGestureOptions({ direction: PanDirection.Left })))
}
.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%')
}
}
@Component
struct OrderList {
@Prop type: string
build() {
Column() {
Column({ space: 30 }) {
Row() {
Text('文本' + this.type)
}
Row() {
Text('文本' + this.type)
}
Row() {
Text('文本' + this.type)
}
}
.width('100%')
.height(150)
List() {
ListItem() {
Row() {
Text('取消订单' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('查看发票' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('申请发票' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('退换货' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
ListItem() {
Row() {
Text('去支付' + this.type).fontSize(13).fontColor('#222427')
}
.padding({
left: 12,
right: 12,
top: 5,
bottom: 5
})
.border({
width: 1,
radius: 4,
color: '#C9CED3',
style: BorderStyle.Solid
})
}
}
.listDirection(Axis.Horizontal)
.scrollBar(BarState.Off)
.edgeEffect(EdgeEffect.None)
.width('100%')
.nestedScroll({
scrollForward: NestedScrollMode.SELF_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
}
.width('100%')
}
}
四、nestedScroll介绍
nestedScroll:设置向前向后两个方向上的嵌套滚动模式,实现与父组件的滚动联动。
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
value | NestedScrollOptions | 是 | 嵌套滚动选项。 |
NestedScrollOptions对象说明
名称 | 类型 | 必填 | 描述 |
---|---|---|---|
scrollForward | NestedScrollMode | 是 | 滚动组件往末尾端滚动时的嵌套滚动选项。 |
scrollBackward | NestedScrollMode | 是 | 滚动组件往起始端滚动时的嵌套滚动选项。 |
NestedScrollMode枚举说明
名称 | 描述 |
---|---|
SELF_ONLY | 只自身滚动,不与父组件联动。 |
SELF_FIRST | 自身先滚动,自身滚动到边缘以后父组件滚动。父组件滚动到边缘以后,如果父组件有边缘效果,则父组件触发边缘效果,否则子组件触发边缘效果。 |
PARENT_FIRST | 父组件先滚动,父组件滚动到边缘以后自身滚动。自身滚动到边缘后,如果有边缘效果,会触发自身的边缘效果,否则触发父组件的边缘效果。 |
PARALLEL | 自身和父组件同时滚动,自身和父组件都到达边缘以后,如果自身有边缘效果,则自身触发边缘效果,否则父组件触发边缘效果。 |