refreshcontrol react flatlist example react-native

flatlist - Cómo desplazarse hasta la parte inferior de React Native ListView



refreshcontrol react native example (11)

A partir de React Native 0.41, tanto ListView como ScrollView tienen métodos scrollToEnd() . ListView está documentado en https://facebook.github.io/react-native/docs/listview.html#scrolltoend

Deberá usar una ref para almacenar una referencia a su ListView cuando lo represente:

<ListView dataSource={yourDataSource} renderRow={yourRenderingCallback} ref={listView => { this.listView = listView; }} </ListView>

Luego, puede simplemente llamar this.listView.scrollToEnd() para desplazarse al final de la lista. Si desea hacer esto cada vez que cambia el contenido de ListView (por ejemplo, cuando se agrega contenido), hágalo desde la función onContentSizeChange prop de ListView , al igual que con ScrollView .

Estoy escribiendo una aplicación usando React Native. Tiene un ListView para mostrar la lista de elementos.

Añado nuevos elementos en la parte inferior cuando hay nuevos disponibles. Me gustaría desplazarme hacia abajo en el ListView para mostrar los nuevos elementos automáticamente. ¿Cómo puedo hacer esto?


Aquí hay otra variación. Personalmente estoy usando esto para desplazarme hacia la parte inferior de una vista de lista cuando alguien comenta. Lo prefiero a los otros ejemplos, ya que es más conciso.

listViewHeight se puede determinar a través de varios medios, pero personalmente lo obtengo de una animación que se usa para animar la altura de la vista de lista para que se salga del camino del teclado.

render() { let initialListSize = 5 if (this.state.shouldScrollToBottom) { initialListSize = 100000 // A random number that''s going to be more rows than we''re ever going to see in the list. } return (<ListView ref="listView" initialListSize={ initialListSize } otherProps... renderFooter={() => { return <View onLayout={(event)=>{ if (this.state.shouldScrollToBottom) { const listViewHeight = this.state.height._value this.refs.listView.scrollTo({y: event.nativeEvent.layout.y - listViewHeight + 64}) // 64 for iOS to offset the nav bar height this.setState({shouldScrollToBottom: false}) } }}></View> }} />) } scrollToBottom() { self.setState({shouldScrollToBottom: true}) }


Consulte esto: https://github.com/650Industries/react-native-invertible-scroll-view

Este componente invierte la vista de desplazamiento original. Entonces, cuando lleguen nuevos artículos, se desplazará automáticamente a la parte inferior.

Sin embargo, tenga en cuenta dos cosas

  1. Su "matriz de datos" también debe revertirse. Es decir, debería ser

    [new_item, old_item]

    y cualquier artículo nuevo que llegue se debe insertar en la parte frontal.

  2. Aunque usan ListView en su ejemplo de README, todavía hay algunos defectos al usar este complemento con ListView . En cambio, te sugiero que solo uses ScrollView , que funciona bastante bien.

Un ejemplo para la vista de desplazamiento invertido:

var MessageList = React.createClass({ propTypes: { messages: React.PropTypes.array, }, renderRow(message) { return <Text>{message.sender.username} : {message.content}</Text>; }, render() { return ( <InvertibleScrollView onScroll={(e) => console.log(e.nativeEvent.contentOffset.y)} scrollEventThrottle={200} renderScrollView={ (props) => <InvertibleScrollView {...props} inverted /> }> {_.map(this.props.messages, this.renderRow)} </InvertibleScrollView> ); } });


Entonces, para hacer un desplazamiento automático al final de ListView, debe agregar prop ''onContentSizeChange'' a ListView y tomar el contenido respectivo de su argumento, como este:

<ListView ref=''listView'' onContentSizeChange={(contentWidth, contentHeight) => { this.scrollTo(contentHeight); }} ... />

Por lo tanto, en mi caso, debería presentar mi lista verticalmente, por eso utilicé contentHeight, en el caso de una lista horizontal, solo tenía que usar contentWeight.

Aquí la función scrollTo debería ser:

scrollTo = (y) => { if (y<deviceHeight-120) { this.refs.listView.scrollTo({ y: 0, animated: true }) }else{ let bottomSpacing = 180; if (this.state.messages.length > 0) { bottomSpacing = 120; } this.refs.listView.scrollTo({ y: y - deviceHeight + bottomSpacing, animated: true }) }

}

Eso es. Espero que esta explicación pueda ayudar a alguien a ahorrar tiempo.


Esto es lo que uso con React Native 0.14. Este contenedor ListView se desplaza hacia abajo siempre que:

  • Los cambios de altura del contenido
  • Los cambios de altura del contenedor
  • El teclado se vuelve visible o invisible

Refleja el comportamiento estándar de la mayoría de las aplicaciones de chat.

Esta implementación depende de los detalles de implementación de RN0.14 ListView y, por lo tanto, podría necesitar ajustes para ser compatible con las futuras versiones de React Native.

var React = require(''react-native''); var RCTDeviceEventEmitter = require(''RCTDeviceEventEmitter''); var RCTUIManager = require(''NativeModules'').UIManager; var { ListView, } = React; export default class AutoScrollListView extends React.Component { componentWillMount() { this._subscribableSubscriptions = []; } componentDidMount() { this._addListenerOn(RCTDeviceEventEmitter, ''keyboardWillShow'', this._onKeyboardWillShow); this._addListenerOn(RCTDeviceEventEmitter, ''keyboardWillHide'', this._onKeyboardWillHide); var originalSetScrollContentLength = this.refs.listView._setScrollContentLength; var originalSetScrollVisibleLength = this.refs.listView._setScrollVisibleLength; this.refs.listView._setScrollContentLength = (left, top, width, height) => { originalSetScrollContentLength(left, top, width, height); this._scrollToBottomIfContentHasChanged(); }; this.refs.listView._setScrollVisibleLength = (left, top, width, height) => { originalSetScrollVisibleLength(left, top, width, height); this._scrollToBottomIfContentHasChanged(); }; } componentWillUnmount() { this._subscribableSubscriptions.forEach( (subscription) => subscription.remove() ); this._subscribableSubscriptions = null; } render() { return } _addListenerOn = (eventEmitter, eventType, listener, context) => { this._subscribableSubscriptions.push( eventEmitter.addListener(eventType, listener, context) ); } _onKeyboardWillShow = (e) => { var animationDuration = e.duration; setTimeout(this._forceRecalculationOfLayout, animationDuration); } _onKeyboardWillHide = (e) => { var animationDuration = e.duration; setTimeout(this._forceRecalculationOfLayout, animationDuration); } _forceRecalculationOfLayout = () => { requestAnimationFrame(() => { var scrollComponent = this.refs.listView.getScrollResponder(); if (!scrollComponent || !scrollComponent.getInnerViewNode) { return; } RCTUIManager.measureLayout( scrollComponent.getInnerViewNode(), React.findNodeHandle(scrollComponent), () => {}, //Swallow error this.refs.listView._setScrollContentLength ); RCTUIManager.measureLayoutRelativeToParent( React.findNodeHandle(scrollComponent), () => {}, //Swallow error this.refs.listView._setScrollVisibleLength ); }); } _scrollToBottomIfContentHasChanged = () => { var scrollProperties = this.refs.listView.scrollProperties; var hasContentLengthChanged = scrollProperties.contentLength !== this.previousContentLength; var hasVisibleLengthChanged = scrollProperties.visibleLength !== this.previousVisibleLength; this.previousContentLength = scrollProperties.contentLength; this.previousVisibleLength = scrollProperties.visibleLength; if(!hasContentLengthChanged && !hasVisibleLengthChanged) { return; } this.scrollToBottom(); } scrollToBottom = () => { var scrollProperties = this.refs.listView.scrollProperties; var scrollOffset = scrollProperties.contentLength - scrollProperties.visibleLength; requestAnimationFrame(() => { this.refs.listView.getScrollResponder().scrollTo(scrollOffset); }); } }


Hay una solución bastante simple para este problema. Puede envolver su ListView dentro de un componente Scrollview. Esto proporcionará todos los métodos necesarios para determinar la posición inferior de su lista.

Primero envuelve tu listaVer

<ScrollView> <MyListViewElement /> </ScrollView>

Luego use el método onLayout que devuelve la altura del componente (scrollView). y guárdalo en el estado.

// add this method to the scrollView component onLayout={ (e) => { // get the component measurements from the callbacks event const height = e.nativeEvent.layout.height // save the height of the scrollView component to the state this.setState({scrollViewHeight: height }) }}

A continuación, utilice el método onContentSizeChange que devuelve la altura de los componentes internos (listView). y guárdalo en el estado. Esto sucederá cada vez que agregue o elimine un elemento de la lista, o cambie la altura. Esencialmente cada vez que alguien agrega un nuevo mensaje a su lista.

onContentSizeChange={ (contentWidth, contentHeight) => { // save the height of the content to the state when there’s a change to the list // this will trigger both on layout and any general change in height this.setState({listHeight: contentHeight }) }}

Para desplazarse, necesitará el método scrollTo que se encuentra dentro de ScrollView. Puede acceder a esto guardándolo en la ref en el estado como tal.

<ScrollView ref={ (component) => this._scrollView = component } … > </ScrollView>

Ahora tiene todo lo que necesita para calcular y activar su desplazamiento al final de la lista. Puede elegir hacer esto en cualquier lugar de su componente, lo agregaré a componentDidUpdate() modo que cada vez que se represente el componente, se scrollTo la parte inferior.

componentDidUpdate(){ // calculate the bottom const bottomOfList = this.state.listHeight - this.state.scrollViewHeight // tell the scrollView component to scroll to it this.scrollView.scrollTo({ y: bottomOfList }) }

Y eso es. Así es como debería verse tu ScrollView al final

<ScrollView ref={ (component) => this._scrollView = component } onContentSizeChange={ (contentWidth, contentHeight) => { this.setState({listHeight: contentHeight }) }} onLayout={ (e) => { const height = e.nativeEvent.layout.heigh this.setState({scrollViewHeight: height }) }} > <MyListViewElement /> </ScrollView>

UNA MANERA MÁS SIMPLE Estaba usando un ListView y me pareció mucho más fácil hacer esto al usar un scrollView, por simplicidad, lo recomiendo. Aquí hay una copia directa de mi módulo de Mensajes para la función de desplazamiento hacia abajo. Espero eso ayude.

class Messages extends Component { constructor(props){ super(props) this.state = { listHeight: 0, scrollViewHeight: 0 } } componentDidUpdate(){ this.scrollToBottom() } scrollToBottom(){ const bottomOfList = this.state.listHeight - this.state.scrollViewHeight console.log(''scrollToBottom''); this.scrollView.scrollTo({ y: bottomOfList }) } renderRow(message, index){ return ( <Message key={message.id} {...message} /> ); } render(){ return( <ScrollView keyboardDismissMode="on-drag" onContentSizeChange={ (contentWidth, contentHeight) => { this.setState({listHeight: contentHeight }) }} onLayout={ (e) => { const height = e.nativeEvent.layout.height this.setState({scrollViewHeight: height }) }} ref={ (ref) => this.scrollView = ref }> {this.props.messages.map( message => this.renderRow(message) )} </ScrollView> ) } } export default Messages


Me gustó la mejor solución de @ComethTheNerd, y aquí hay una versión más común (pasó todas las reglas de ESlinting de airbnb):

state={ listHeight: 0, footerY: 0, } // dummy footer to ascertain the Y offset of list bottom renderFooter = () => ( <View onLayout={(event) => { this.setState({ footerY: event.nativeEvent.layout.y }); }} /> );

...

<ListView ref={(listView) => { this.msgListView = listView; }} renderFooter={this.renderFooter} onLayout={(event) => { this.setState({ listHeight: event.nativeEvent.layout.height }); }} />

E invoque el método scrollToBottom siguiente manera:

componentDidUpdate(prevProps, prevState) { if (this.state.listHeight && this.state.footerY && this.state.footerY > this.state.listHeight) { // if content is longer than list, scroll to bottom this.scrollToBottom(); } } scrollToBottom = () => { const scrollDistance = this.state.footerY - this.state.listHeight; this.msgListView.scrollTo({ y: scrollDistance, animated: true }); }


Me temo que no hay una manera ultra limpia de hacer esto. Por lo tanto, lo haremos manualmente, no se preocupe, es fácil y no es complicado.

Paso 1 : declare esas variables en su estado

constructor(props) { super(props); this.state={ lastRowY:0, } }

Paso 2 : crea la función scrollToBottom así:

scrollToBottom(){ if(!!this.state.lastRowY){ let scrollResponder = this.refs.commentList.getScrollResponder(); scrollResponder.scrollResponderScrollTo({x: 0, y: this.state.lastRowY, animated: true}); } }

Paso 3 : agregue las siguientes propiedades a su ListView :

  • Un ref para poder acceder a él (en este ejemplo, commentList )

    ref="commentList"

  • La siguiente función onLayout dentro de la barra de representación, en su elemento de fila:

    onLayout={(event) => { var {y} = event.nativeEvent.layout; this.setState({ lastRowY : y });

Tu ListView debería verse más o menos así:

<ListView ref="commentList" style={styles.commentsContainerList} dataSource={this.state.commentDataSource} renderRow={()=>( <View onLayout={(event)=>{ let {y} = event.nativeEvent.layout; this.setState({ lastRowY : y }); }} /> </View> )} />

Paso 4 : Luego, en cualquier parte de tu código, simplemente llama a this.scrollToBottom(); .

Disfrutar..


Para aquellos que están interesados ​​en implementarlo simplemente con ListView

// initialize necessary variables componentWillMount() { // initialize variables for ListView bottom focus this._content_height = 0; this._view_height = 0; } render() { return ( <ListView ...other props onLayout = {ev => this._scrollViewHeight = ev.nativeEvent.layout.height} onContentSizeChange = {(width, height)=>{ this._scrollContentHeight = height; this._focusOnBottom(); }} renderScrollComponent = {(props) => <ScrollView ref = {component => this._scroll_view = component} onLayout = {props.onLayout} onContentSizeChange = {props.onContentSizeChange} /> } /> ); } _focusOnLastMessage() { const bottom_offset = this._content_height - this._view_height; if (bottom_offset > 0 &&) this._scroll_view.scrollTo({x:0, y:scrollBottomOffsetY, false}); }

Puedes usar la función _focusOnLastMessage donde quieras, por ejemplo, la uso siempre que cambie el tamaño del contenido. Probé los códigos con [email protected]


Resuelvo esto para ScrollView . Aquí hay un ejemplo simple:

class MessageList extends Component { componentDidUpdate() { let innerScrollView = this._scrollView.refs.InnerScrollView; let scrollView = this._scrollView.refs.ScrollView; requestAnimationFrame(() => { innerScrollView.measure((innerScrollViewX, innerScrollViewY, innerScrollViewWidth, innerScrollViewHeight) => { scrollView.measure((scrollViewX, scrollViewY, scrollViewWidth, scrollViewHeight) => { var scrollTo = innerScrollViewHeight - scrollViewHeight + innerScrollViewY; if (innerScrollViewHeight < scrollViewHeight) { return; } this._scrollView.scrollTo(scrollTo); }); }); }); } render() { return ( <ScrollView ref={component => this._scrollView = component}> {this.props.messages.map((message, i) => { return <Text key={i}>{message}</Text>; })} </ScrollView> ); } }

Gracias por @ccheever


Tuve el mismo problema, y ​​se me ocurrió esta solución:

render() { if("listHeight" in this.state && "footerY" in this.state && this.state.footerY > this.state.listHeight) { var scrollDistance = this.state.listHeight - this.state.footerY; this.refs.list.getScrollResponder().scrollTo(-scrollDistance); } return ( <ListView ref="list" onLayout={(event) => { var layout = event.nativeEvent.layout; this.setState({ listHeight : layout.height }); }} renderFooter={() => { return <View onLayout={(event)=>{ var layout = event.nativeEvent.layout; this.setState({ footerY : layout.y }); }}></View> }} /> ) }

Básicamente, presento un pie de página vacío para determinar el desplazamiento en Y de la lista inferior. De esto puedo derivar el desplazamiento de desplazamiento hacia abajo, según la altura del contenedor de lista.

NOTA: La última condición if comprueba si la longitud del contenido sobrepasa la altura de la lista y solo se desplaza si lo hace. Si lo necesita o no, ¡depende de su diseño!

Espero que esta solución ayude a otros en la misma posición.

FWIW No me InvertibleScrollView complemento InvertibleScrollView discutido en otra respuesta porque hace una transformación de escala en toda la lista, y cada elemento de la lista ... ¡que suena caro!