免责声明:以下内容主要是我自己在 React Native 0.50 中的实验结果。该ScrollView
文档目前缺少以下涵盖的许多信息;例如onScrollEndDrag
完全没有记录。由于这里的一切都依赖于未记录的行为,不幸的是,我不能保证这些信息在一年甚至一个月后仍然正确。
此外,下面的所有内容都假设我们感兴趣的y偏移量是纯垂直滚动视图;如果需要,转换为x偏移量对读者来说是一个简单的练习。
ScrollView
take an上的各种事件处理程序event
,让您通过event.nativeEvent.contentOffset.y
. 其中一些处理程序在 Android 和 iOS 之间的行为略有不同,详情如下。
在安卓上
在用户滚动时触发每一帧,在用户释放滚动视图后滑动时触发每一帧,当滚动视图静止时在最后一帧触发,以及当滚动视图的偏移量因其帧而改变时触发变化(例如,由于从横向旋转到纵向)。
在 iOS 上
在用户拖动或滚动视图滑动时触发,频率由 确定,scrollEventThrottle
并且最多每帧一次scrollEventThrottle={16}
。如果用户在滚动视图有足够的滑行动量时释放它,则onScroll
处理程序也会在滑行后静止时触发。但是,如果用户在静止时拖动然后释放滚动视图,onScroll
则不能保证在最终位置触发,除非scrollEventThrottle
已设置为onScroll
触发每一帧滚动。
设置的性能成本scrollEventThrottle={16}
可以通过将其设置为更大的数字来降低。但是,这意味着onScroll
不会每帧都触发。
当滚动视图在滑动后停止时触发。如果用户在它静止时释放滚动视图,则它根本不会触发,这样它就不会滑动。
onScrollEndDrag
当用户停止拖动滚动视图时触发 - 无论滚动视图是保持静止还是开始滑动。
鉴于这些行为差异,跟踪偏移的最佳方法取决于您的具体情况。在最复杂的情况下(你需要支持Android和iOS,包括处理ScrollView
由于旋转导致的frame的变化,你不想接受Android上从设置scrollEventThrottle
到16的性能损失),你需要处理滚动视图中的内容也发生了变化,那就是一团糟。
最简单的情况是,如果您只需要处理 Android;只需使用onScroll
:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
>
为了额外支持 iOS,如果您乐于在onScroll
每一帧触发处理程序并接受其性能影响,并且如果您不需要处理帧更改,那么它只是稍微复杂一点:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={16}
>
为了减少 iOS 上的性能开销,同时仍然保证我们记录滚动视图所定位的任何位置,我们可以增加scrollEventThrottle
并额外提供一个onScrollEndDrag
处理程序:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={160}
>
但是如果我们想要处理框架变化(例如,因为我们允许设备旋转,改变滚动视图框架的可用高度)和/或内容变化,那么我们必须另外实现两者onContentSizeChange
并onLayout
跟踪两者的高度滚动视图的框架及其内容,从而不断计算最大可能的偏移量并推断偏移量何时因框架或内容大小更改而自动减小:
<ScrollView
onLayout={event => {
this.frameHeight = event.nativeEvent.layout.height;
const maxOffset = this.contentHeight - this.frameHeight;
if (maxOffset < this.yOffset) {
this.yOffset = maxOffset;
}
}}
onContentSizeChange={(contentWidth, contentHeight) => {
this.contentHeight = contentHeight;
const maxOffset = this.contentHeight - this.frameHeight;
if (maxOffset < this.yOffset) {
this.yOffset = maxOffset;
}
}}
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y;
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y;
}}
scrollEventThrottle={160}
>
是的,这太可怕了。我也不能 100% 确定它在您同时更改滚动视图的框架和内容的大小的情况下始终正常工作。但这是我能想到的最好的,在这个功能被添加到框架本身之前,我认为这是任何人都能做到的最好的。