Isenção de responsabilidade: o que se segue é principalmente o resultado de minhas próprias experiências no React Native 0.50. A ScrollView
documentação está faltando muitas das informações cobertas abaixo; por exemplo, onScrollEndDrag
é completamente indocumentado. Como tudo aqui depende de comportamento não documentado, infelizmente não posso prometer que essa informação permanecerá correta daqui a um ano ou até um mês.
Além disso, tudo a seguir assume uma visualização de rolagem puramente vertical, em cujo deslocamento y estamos interessados; traduzir para x offsets, se necessário, é um exercício fácil para o leitor.
Vários manipuladores de eventos em um ScrollView
assumir um event
e deixá-lo começar a posição de rolagem atual via event.nativeEvent.contentOffset.y
. Alguns desses manipuladores têm um comportamento ligeiramente diferente entre Android e iOS, conforme detalhado abaixo.
No Android
Dispara cada quadro enquanto o usuário está rolando, em cada quadro enquanto a visualização de rolagem está deslizando depois que o usuário a libera, no quadro final quando a visualização de rolagem pára e também sempre que o deslocamento da visualização de rolagem muda como resultado de seu quadro mudança (por exemplo, devido à rotação de paisagem para retrato).
No iOS
Dispara enquanto o usuário está arrastando ou enquanto a visualização de rolagem está deslizando, em alguma frequência determinada por scrollEventThrottle
e no máximo uma vez por quadro quando scrollEventThrottle={16}
. Se o usuário liberar a visualização de rolagem enquanto ela tem impulso suficiente para deslizar, o onScroll
manipulador também disparará quando chegar ao repouso após deslizar. No entanto, se o usuário arrasta e, em seguida, libera a exibição de rolagem enquanto ele está parado, onScroll
é não garantido para o fogo para a posição final, a menos que scrollEventThrottle
tenha sido definida de tal forma que onScroll
incêndios cada quadro de rolagem.
Há um custo de desempenho na configuração scrollEventThrottle={16}
que pode ser reduzido configurando-o para um número maior. No entanto, isso significa que onScroll
não disparará todos os quadros.
Dispara quando a visualização de rolagem para após deslizar. Não dispara se o usuário liberar a visualização de rolagem enquanto ela estiver parada de forma que não deslize.
onScrollEndDrag
Dispara quando o usuário para de arrastar a visualização de rolagem - independentemente de a visualização de rolagem ficar parada ou começar a deslizar.
Dadas essas diferenças de comportamento, a melhor maneira de controlar o deslocamento depende de suas circunstâncias precisas. No caso mais complicado (você precisa suportar Android e iOS, incluindo manipulação de mudanças no ScrollView
quadro de devido à rotação, e você não quer aceitar a penalidade de desempenho no Android da configuração scrollEventThrottle
para 16), e você precisa lidar com muda para o conteúdo na visualização de rolagem também, então é uma bagunça certa.
O caso mais simples é se você só precisa lidar com o Android; apenas use onScroll
:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
>
Para oferecer suporte adicional ao iOS, se você estiver feliz em disparar o onScroll
manipulador em todos os quadros e aceitar as implicações de desempenho disso, e se não precisar lidar com mudanças de quadro, então é apenas um pouco mais complicado:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={16}
>
Para reduzir a sobrecarga de desempenho no iOS e, ao mesmo tempo, garantir que registremos qualquer posição em que a visualização de rolagem se estabelece, podemos aumentar scrollEventThrottle
e fornecer adicionalmente um onScrollEndDrag
manipulador:
<ScrollView
onScroll={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
onScrollEndDrag={event => {
this.yOffset = event.nativeEvent.contentOffset.y
}}
scrollEventThrottle={160}
>
Mas se quisermos lidar com mudanças de quadro (por exemplo, porque permitimos que o dispositivo seja girado, mudando a altura disponível para o quadro da visualização de rolagem) e / ou mudanças de conteúdo, devemos implementar adicionalmente ambos onContentSizeChange
e onLayout
manter o controle da altura de ambos o quadro da visualização de rolagem e seu conteúdo e, assim, calcular continuamente o deslocamento máximo possível e inferir quando o deslocamento foi reduzido automaticamente devido a uma alteração no tamanho do quadro ou do conteúdo:
<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}
>
Sim, é horrível. Também não estou 100% certo de que sempre funcionará bem nos casos em que você alterar simultaneamente o tamanho do quadro e o conteúdo da visualização de rolagem. Mas é o melhor que posso inventar e, até que esse recurso seja adicionado ao próprio framework , acho que é o melhor que alguém pode fazer.