Отказ от ответственности: все, что следует ниже, в первую очередь является результатом моих собственных экспериментов с React Native 0.50. В ScrollView
документации в настоящее время отсутствует большая часть информации, описанной ниже; например onScrollEndDrag
, полностью недокументировано. Поскольку все здесь основано на недокументированном поведении, я, к сожалению, не могу обещать, что эта информация останется верной через год или даже через месяц.
Кроме того, все, что ниже, предполагает чисто вертикальную прокрутку, чье смещение по y нас интересует; перевод в x смещения, если необходимо, будет легким упражнением для читателя.
Различные обработчики событий на ScrollView
Берут event
и позволяют получить текущую позицию прокрутки с помощью event.nativeEvent.contentOffset.y
. Некоторые из этих обработчиков немного различаются между Android и iOS, как описано ниже.
На Android
Запускает каждый кадр, пока пользователь прокручивает, на каждом кадре, когда представление прокрутки скользит после того, как пользователь отпускает его, на последнем кадре, когда представление прокрутки останавливается, а также всякий раз, когда смещение представления прокрутки изменяется в результате его кадра изменение (например, из-за поворота с пейзажа на портрет).
На iOS
Срабатывает, когда пользователь перетаскивает или скользит вид прокрутки, с некоторой частотой, определяемой scrollEventThrottle
и не более одного раза за кадр, когда scrollEventThrottle={16}
. Если пользователь отпускает режим прокрутки, пока у него достаточно импульса для скольжения, onScroll
обработчик также сработает, когда он остановится после скольжения. Однако, если пользователь перетаскивает , а затем отпускает вид прокрутки , пока она находится в неподвижном состоянии , onScroll
это не гарантирует огня для конечной позиции , если scrollEventThrottle
не было установлено , что такие onScroll
пожары каждый кадр прокрутки.
Установка scrollEventThrottle={16}
требует снижения производительности, которую можно уменьшить, задав большее значение. Однако это означает, что onScroll
не будет запускаться каждый кадр.
Срабатывает, когда режим прокрутки останавливается после скольжения. Не срабатывает вообще, если пользователь отпускает прокрутку, когда она неподвижна и не скользит.
onScrollEndDrag
Срабатывает, когда пользователь перестает перетаскивать вид прокрутки - независимо от того, остается ли вид прокрутки неподвижным или начинает скользить.
Учитывая эти различия в поведении, лучший способ отслеживать смещение зависит от конкретных обстоятельств. В самом сложном случае (вам необходимо поддерживать Android и iOS, в том числе обрабатывать изменения в ScrollView
кадре из-за вращения, и вы не хотите соглашаться с потерей производительности на 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%, что он всегда будет работать правильно в тех случаях, когда вы одновременно изменяете размер как рамки, так и содержимого представления прокрутки. Но это лучшее, что я могу придумать, и пока эта функция не будет добавлена в сам фреймворк , я думаю, что это лучшее, что может сделать каждый.