前言
本文基于Api13
在实际的开发中,滑动冲突在所难免,常见于可滚动的容器组件嵌套使用,比如Scroll组件嵌套List组件,List组件嵌套了Grid组件等等,只要是滚动容器组件嵌套,令人厌烦的滑动冲突就会扑面而来,十分让人抓狂;如何处理滑动冲突,成了开发中的必修之课,然而,鸿蒙当中的处理方式,相对与Android端而言,似乎更加的直观和简单,使用一个属性便可以搞定,那就是nestedScroll。
简单案例
案例很是简单,外层是一个Scroll滚动容器,内部也有一个List组件滚动组件,在List上面有一个普通的组件。
@Entry
@Component
struct Index {
build() {
Scroll() {
Column() {
Text("我是顶部的组件")
.width("100%")
.height(200)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Pink)
.fontColor(Color.White)
List() {
ForEach([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], (item: number) => {
ListItem() {
Text(item.toString())
.width("100%")
.height(80)
.textAlign(TextAlign.Center)
}
})
}
.width("100%")
.height("100%")
.scrollBar(BarState.Off)
.divider({ color: Color.Gray, strokeWidth: 1 })
.edgeEffect(EdgeEffect.None)
}
}.width("100%")
.height("100%")
.scrollBar(BarState.Off)
}
}
效果如下所示:
我们想要的效果是,先让顶部的组件滚动上去,然后再滚动下面的List组件,我们运行看下效果:
显然未达到我们的效果,这就是滑动冲突导致的,这种情况下,做为子组件的List组件,一定是先把滑动事件交给Scroll组件,等Scroll组件滚动到边缘后,再把滑动事件交给自己,这样的流程也是正确的,如何做到呢,前边已经说过了,那就是nestedScroll属性。
每个滚动容器组件都有一个nestedScroll属性,主要解决嵌套滚动模式下的的滑动冲突,我们给List组件设置一下,代码如下:
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
设置完后,再看下效果:
可以看到,已经完美的解决了滑动冲突,达到了我们想要的效果。
了解nestedScroll
通过前言中的案例,我们基本上已经知道了nestedScroll属性的作用,主要是,用于设置嵌套滚动选项,设置前后两个方向的嵌套滚动模式,实现与父组件的滚动联动。
我们重点关注一下NestedScrollOptions这个参数,其中有两个值,分别是scrollForward和scrollBackward,一个代表往末尾端滚动,一个代表往起始端滚动。
两个参数都有一个共同的值类型NestedScrollMode,可供选择的有以下几个值:
名称 | 说明 |
---|---|
SELF_ONLY | 只自身滚动,不与父组件联动。 |
SELF_FIRST | 自身先滚动,自身滚动到边缘以后父组件滚动。父组件滚动到边缘以后,如果父组件有边缘效果,则父组件触发边缘效果,否则子组件触发边缘效果。 |
PARENT_FIRST | 父组件先滚动,父组件滚动到边缘以后自身滚动。自身滚动到边缘后,如果有边缘效果,会触发自身的边缘效果,否则触发父组件的边缘效果。 |
PARALLEL | 自身和父组件同时滚动,自身和父组件都到达边缘以后,如果自身有边缘效果,则自身触发边缘效果,否则父组件触发边缘效果。 |
举例使用
前言中,我们简单使用Scroll组件嵌套List组件举了一个例子,我们再来看一个Scroll组件嵌套Web组件的案例。
import webview from '@ohos.web.webview'
@Entry
@Component
struct Index {
private controller: webview.WebviewController = new webview.WebviewController()
build() {
Scroll() {
Column() {
Text("我是顶部的组件")
.width("100%")
.height(200)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Pink)
.fontColor(Color.White)
Web({
src: "https://juejin.cn/user/1398234520239095/columns",
controller: this.controller
})
.nestedScroll({
scrollForward: NestedScrollMode.PARENT_FIRST,
scrollBackward: NestedScrollMode.SELF_FIRST
})
}
}.width("100%")
.height("100%")
.scrollBar(BarState.Off)
}
}
以上的效果和前言中基本一致,不在贴效果了,其实再多的案例,基本上都是一样的,只是nestedScroll中的值不一样而已。
相关总结
在实际的开发中,远远要比前边的案例复杂,比如我之前封装的刷新库,滚动组件上面有刷新头,下面也有加载尾,还有更复杂的,列表吸顶操作等等,一个nestedScroll属性是远远不够的,往往还要和滑动监听,是否滚动到了顶部和尾部等相结合,才能实现我们实际的效果。
比如,还是以上的案例,我们在List组件下面再增加一个底部的组件,我们运行看下效果:
显然之前配置的nestedScroll属性只满足了顶部的组件,而底部的组件在滑动的过程中明显有滑动冲突,并没有达到我们的预期,其实对于底部,上滑是先滚动自己后滚动父组件,下滑是要先滚动父组件,然后再去滚动自身组件的。
针对以上的问题,其实处理有多种方式,最简单的莫过于,底部组件在最后一个组件下面,当做List组件的部分,当然了顶部组件也可以这么去做,这样就不存在滑动冲突。
if (index == this.dataArray.length - 1) {
Column() {
Text(item.toString())
.width("100%")
.height(80)
.textAlign(TextAlign.Center)
Text("我是底部的组件")
.width("100%")
.height(200)
.textAlign(TextAlign.Center)
.backgroundColor(Color.Pink)
.fontColor(Color.White)
}
}
我们看下效果:
把组件加到首尾数据的位置,这种方式会存在一个问题,比如说,如果没有数据呢?顶部组件和底部组件那么就无法显示,再比如如果加载的不是List组件,而是一个Grid组件或者其他的滚动组件呢?显然处理上有很大的问题。
这个作业留给友友们,你们知道如何解决吗?