Ist es möglich, eine ScrollView nach unten zu scrollen?


86

Für eine Chat-ähnliche App möchte ich eine ScrollViewKomponente nach unten scrollen lassen, da die neuesten Nachrichten unter den älteren angezeigt werden. Können wir die Bildlaufposition von a anpassen ScrollView?


3
Ich weiß nichts über React-Native, aber denken Sie daran, dass Benutzer manchmal nach oben scrollen möchten, um den Chat-Verlauf anzuzeigen. Stellen Sie sicher, dass Sie den Chat nur scrollen, wenn der Chat bereits ganz nach unten gescrollt wurde, wenn eine neue Nachricht eintrifft.
David

Das ist eine gute Idee. Wir verfolgen dieses Problem hier: github.com/facebook/react-native/issues/107
Eric Vicenti

Ich habe gerade eine ähnliche Frage beantwortet, bitte überprüfen Sie sie hier - stackoverflow.com/a/32652967/1541508
Kohver

Antworten:


161

Für React Native 0.41 und höher können Sie dies mit der integrierten scrollToEndMethode tun :

<ScrollView
    ref={ref => {this.scrollView = ref}}
    onContentSizeChange={() => this.scrollView.scrollToEnd({animated: true})}>
</ScrollView>

3
Ab reaktionsnativ 0,55,4 und höher musste ich rootsonst verwenden, scrollToEnd ist undefiniert. dhthis.scrollView.root.scrollToEnd
Simon

4
Die Verwendung von react native 0.58.6 und using löst roottatsächlich einen undefinierten Fehler aus.
Jose Bernhardt

1
Sollte dies sein: ref={ref => {this.scrollView = ref}}da Sie die Aufgabe nicht zurückgeben möchten
Hampycalc

25

Verwenden Sie onContentSizeChange, um das untere Y der Bildlaufansicht (Höhe) zu verfolgen.

https://facebook.github.io/react-native/docs/scrollview.html#oncontentsizechange

var _scrollToBottomY

<ScrollView
    onContentSizeChange={(contentWidth, contentHeight)=>{
    _scrollToBottomY = contentHeight;
    }}>
</ScrollView>

Verwenden Sie dann den Referenznamen der Bildlaufansicht wie

this.refs.scrollView.scrollTo(_scrollToBottomY);

2
Beachten Sie, dass dadurch die Ansicht vollständig nach unten gescrollt wird, sodass die Ansicht im Wesentlichen nicht sichtbar ist (es sei denn, Sie haben nach der Messung etwas hinzugefügt). Dies ist sinnvoll, da der Code zur Höhe des scrollbaren Bereichs scrollt und nicht zur Höhe der Kinder, die den scrollbaren Bereich überlaufen (wahrscheinlich NICHT das, was OP wollte). Siehe stattdessen stackoverflow.com/a/39563193/1526986 .
Xixixao

20

Hier ist die einfachste Lösung, die ich aufbringen könnte:

 <ListView
ref={ref => (this.listView = ref)}
onLayout={event => {
    this.listViewHeight = event.nativeEvent.layout.height;
}}
onContentSizeChange={() => {
    this.listView.scrollTo({
        y: this.listView.getMetrics().contentLength - this.listViewHeight
    });
}}
/>;

1
Wenn ich diese oder ähnliche Antworten verwende, verschwinden die Elemente in der Liste (es sieht so aus, als würde es zu weit scrollen , aber ich kann nicht sicher sein). Wenn Sie ein kleines bisschen scrollen, wird alles wieder angezeigt, und es sieht so aus, als würde es wieder in Sichtweite geraten (daher das Scrollen zu weit). Irgendeine offensichtliche Idee, warum das sein könnte?
Tscizzle

9

Für alle, die Funktionskomponenten schreiben, die Hook verwenden können,

import React, { useRef } from 'react';
import { ScrollView } from 'react-native';

const ScreenComponent = (props) => {
  const scrollViewRef = useRef();
  return (
    <ScrollView
      ref={scrollViewRef}
      onContentSizeChange={() => scrollViewRef.current.scrollToEnd({ animated: true })}
    />
  );
};

5

Versuchen Sie diese Lösung

xcode 10.0

RN: 56.0.0

<ScrollView ref="scrollView"
             onContentSizeChange={(width,height) => this.refs.scrollView.scrollTo({y:height})}>  </ScrollView> // OR height -  width

warum ref=? scheint wie ein Web-
Entwickler-

4

Sie können die Bildlauf- / Listenansicht mithilfe des react-native-invertible-scroll-viewModuls umkehren und dann einfach aufrufen, ref.scrollTo(0)um nach unten zu scrollen.

Installieren Sie das Modul:

npm install --save react-native-invertible-scroll-view

Importieren Sie dann die Komponente:

import InvertibleScrollView from 'react-native-invertible-scroll-view';

Verwenden Sie dann die folgende JSX anstelle von ScrollView:

<InvertibleScrollView inverted
    ref={ref => { this.scrollView = ref; }}
    onContentSizeChange={() => {
        this.scrollView.scrollTo({y: 0, animated: true});
    }}
>
    { /* content */ }
</InvertibleScrollView>

Dies funktioniert, weil react-native-invertible-scroll-viewder gesamte Inhalt der Ansicht umgedreht wird. Beachten Sie, dass die untergeordneten Elemente der Ansicht auch in der entgegengesetzten Reihenfolge zu einer normalen Ansicht gerendert werden. Das erste untergeordnete Element wird unten angezeigt .


4

Für den Fall, dass sich jemand im Jahr 2020 oder später fragt, wie dies mit Funktionskomponenten erreicht werden kann. So habe ich es gemacht:

ref={ref => scrollView = ref }
onContentSizeChange={() => scrollView.scrollToEnd({ animated: true })}>

Hinweis: Sie können erwähnen, dass Sie useref verwenden. Die meisten Leute wissen nicht, wie man Ref mit Hooks benutzt.
ɐɹƆzɐɹƆ

Ja. Die Sache ist, dass es auch dann funktioniert hat, wenn der useRef-Hook nicht verwendet wird. Aber der richtige Weg ist, es zu benutzen! Sie haben Recht
Marlon Englemam

2

Eigentlich hat die Antwort von @Aniruddha bei mir nicht funktioniert, weil ich sie benutze "react-native": "0.59.8"und this.scrollView.root.scrollToEndauch undefiniert bin

aber ich habe es herausgefunden und das funktioniert this.scrollView.scrollResponderScrollToEnd({animated: true});

Wenn Sie 0.59.8dies verwenden, funktioniert dies ODER wenn Sie eine spätere Version verwenden und dies für Sie funktioniert. Bitte fügen Sie Versionen in diese Antwort ein, damit andere keine Probleme bekommen.

Vollständiger Code hier

<ScrollView
    ref={ref => this.scrollView = ref}
    onContentSizeChange={(contentWidth, contentHeight)=>{        
        this.scrollView.scrollResponderScrollToEnd({animated: true});
    }}>
</ScrollView>

1

Diese Methode ist ein bisschen hacky, aber ich konnte keinen besseren Weg finden

  1. Fügen Sie Ihrer ScrollView zuerst eine Referenz hinzu.
  2. Fügen Sie anschließend eine Dummy-Ansicht hinzu, bevor Sie Ihr ScrollView-Tag schließen
  3. Ermitteln Sie die y-Position dieser Dummy-Ansicht
  4. Wenn Sie nach unten scrollen möchten, scrollen Sie zur Position der Dummy-Ansicht

var footerY; //Y position of the dummy view
render() {    
    return (
      <View>
          <ScrollView 
          ref='_scrollView'>
            // This is where all the things that you want to display in the scroll view goes
            
            // Below is the dummy View
            <View onLayout={(e)=> {
              footerY = e.nativeEvent.layout.y;
              }}/>
          </ScrollView>
              
          //Below is the button to scroll to the bottom    
          <TouchableHighlight
            onPress={() => { this.refs._scrollView.scrollTo(footerY); }}>
            <Text>Scroll to Bottom</Text>
          </TouchableHighlight>
        
      </View>
    );
  }


Dies ist nicht ganz das, wonach gefragt wurde; Dadurch wird beim Tippen nach unten gescrollt, anstatt die Bildlaufansicht beim Hinzufügen von Inhalten nach unten zu scrollen. Auf jeden Fall sind scrollToBottomsolche Ansätze in neueren Versionen von React Native überholt.
Mark Amery

1

Dies beantwortet Ihre Frage: https://stackoverflow.com/a/39563100/1917250

Sie müssen ständig zum Ende der Seite scrollen, indem Sie die Höhe des Inhalts abzüglich der Höhe der scrollView berechnen.


Dies ist die richtige Antwort auf die Frage. Sie können klug sein, wann Sie den Bildlauf ausführen sollen. Die Antwort im Link zeigt Ihnen, wie Sie die benötigten Zahlen erhalten. Insbesondere wenn Sie der Liste Komponenten hinzufügen, werden diese beim Aufrufen von componentDidUpdate in der Inhaltshöhe nicht berücksichtigt. Sie sollten sich also daran erinnern, dass Sie stattdessen auf onContentSizeChange scrollen und scrollen möchten.
Xixixao

1

Wenn Sie TypeScript verwenden, müssen Sie dies tun

interface Props extends TextInputProps { 
     scrollRef?: any;
}

export class Input extends React.Component<Props, any> {
     scrollRef;
constructor(props) {
    this.scrollRef = React.createRef();
}
<ScrollView
    ref={ref => (this.scrollRef = ref)}
    onContentSizeChange={() => {
    this.scrollRef.scrollToEnd();
    }}
>
</ScrollView>

0

Ich habe eine Weile damit gekämpft und mir schließlich etwas ausgedacht, das für mich wirklich gut und reibungslos funktioniert hat. Wie viele habe ich erfolglos versucht .scrollToEnd. Die Lösung, die für mich funktionierte, war eine Kombination aus onContentSizeChange:onLayout Requisiten und ein bisschen mehr denken visuell über „was auf den Boden Scrollen“ wirklich bedeutete. Der letzte Teil scheint mir jetzt trivial, aber ich habe ewig gebraucht, um es herauszufinden.

Da das ScrollVieweine feste Höhe hat, berechnete ich den Versatz, wenn die Inhaltshöhe über die Höhe des Containers geht, indem ich die prevHeight(feste Höhe des ScrollView) für die currHeight(die Inhaltshöhe) subtrahiere . Wenn Sie es sich visuell vorstellen können, schieben Sie beim Scrollen zu diesem Unterschied in der y-Achse die Inhaltshöhe um diesen Versatz über den Container. Ich habe meinen Versatz erhöht und meine jetzt aktuelle Inhaltshöhe zu meiner vorherigen Höhe gemacht und einen neuen Versatz berechnet.

Hier ist der Code. Hoffe es hilft jemandem.

class ChatRoomCorrespondence extends PureComponent {
  currHeight = 0;
  prevHeight = 0;
  scrollHeight = 0;

  scrollToBottom = () => {
    this.refs.scrollView.getScrollResponder().scrollResponderScrollTo({
      x: 0,
      y: this.scrollHeight,
      animated: true
    });
  };

  render() {
    return (
      <ScrollView
        style={Styles.container}
        ref="scrollView"
        onContentSizeChange={(w, h) => {
          this.currHeight = h;

          if (
            this.prevHeight > 0 &&
            this.currHeight - this.scrollHeight > this.prevHeight
          ) {
            this.scrollHeight += this.currHeight - this.prevHeight;
            console.log("--------------------------------------------");
            console.log("Curr: ", this.currHeight);
            console.log("Prev: ", this.prevHeight);
            console.log("Scroll: ", this.scrollHeight);
            this.prevHeight = this.currHeight;
            console.log("PREV: ", this.prevHeight);
            console.log("--------------------------------------------");

            this.scrollToBottom();
          }
        }}
        onLayout={ev => {
          // Fires once
          const fixedContentHeight = ev.nativeEvent.layout.height;
          this.prevHeight = fixedContentHeight;
        }}
      >
        <FlatList
          data={this.props.messages}
          renderItem={({ item }) => (
            <ChatMessage
              text={item.text}
              sender={item.sender}
              time={item.time}
              username={this.props.username}
            />
          )}
          keyExtractor={(item, index) => index.toString()}
        />
      </ScrollView>
    );
  }
}

0

Ich denke, es ist zu spät, um zu antworten, aber ich habe einen Hack! Wenn Sie verwenden FlatListSie aktivieren invertedEigenschaft , die zur Einstellung fällt zurück transform scaleauf -1(die für andere Liste Komponenten wie akzeptabel ist ScrollView...). Wenn Sie Ihre FlatListDaten rendern, werden diese immer ganz unten angezeigt!


-1

Versuchen Sie, die contentOffset- Eigenschaft der Bildlaufansicht auf die aktuelle Höhe des Inhalts festzulegen .

Um das zu erreichen, was Sie benötigen, können Sie dies im Rahmen des onScroll-Ereignisses tun. Dies kann jedoch zu einer schlechten Benutzererfahrung führen, sodass es sich als benutzerfreundlicher erweisen kann, dies nur zu tun, wenn eine neue Nachricht angehängt wird.


-1; Nee. Wie viele der Antworten hier hat dies den Effekt, dass der gesamte Inhalt ScrollViewnach unten gescrollt wird, anstatt nur nach unten zu scrollen.
Mark Amery


Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.