Có thể giữ ScrollView được cuộn xuống dưới cùng không?


87

Đối với một ứng dụng giống như trò chuyện, tôi muốn giữ một ScrollViewthành phần được cuộn xuống dưới cùng, vì các tin nhắn mới nhất xuất hiện bên dưới các tin nhắn cũ hơn. Chúng ta có thể điều chỉnh vị trí cuộn của a ScrollViewkhông?


3
Tôi không biết gì về react-native, nhưng hãy nhớ rằng đôi khi người dùng muốn cuộn lên để xem lịch sử trò chuyện. Đảm bảo chỉ cuộn cuộc trò chuyện nếu cuộc trò chuyện đã được cuộn xuống cuối khi có tin nhắn mới.
David

Đây là một ý tưởng tốt. Chúng tôi đang theo dõi sự cố này tại đây: github.com/facebook/react-native/issues/107
Eric Vicenti

Tôi vừa trả lời một câu hỏi tương tự, vui lòng kiểm tra tại đây - stackoverflow.com/a/32652967/1541508
Kohver

Điều này có trả lời câu hỏi của bạn không? Cách cuộn xuống cuối React Native ListView
TripleM

Câu trả lời:


165

Đối với React Native 0.41 trở lên , bạn có thể thực hiện việc này bằng scrollToEndphương pháp tích hợp:

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

3
Từ react native 0.55.4 trở lên, tôi phải sử dụng rootcách khác, scrollToEnd là không xác định. tức làthis.scrollView.root.scrollToEnd
Simon

4
Hiện đang sử dụng react native 0.58.6 và việc sử dụng rootthực sự tạo ra một lỗi không xác định.
Jose Bernhardt

1
Điều này nên xảy ra: ref={ref => {this.scrollView = ref}}vì bạn không muốn trả lại bài tập
Hampycalc

25

Sử dụng onContentSizeChange để theo dõi phần Y dưới cùng của chế độ xem cuộn (chiều cao)

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

var _scrollToBottomY

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

sau đó sử dụng tên tham chiếu của chế độ xem cuộn như

this.refs.scrollView.scrollTo(_scrollToBottomY);

2
Lưu ý rằng thao tác này sẽ cuộn hoàn toàn chế độ xem xuống dưới cùng, vì vậy chế độ xem về cơ bản không hiển thị (trừ khi bạn thêm thứ gì đó vào sau khi đo). Điều này có ý nghĩa vì mã cuộn đến chiều cao của khu vực có thể cuộn, không phải chiều cao của trẻ em tràn qua khu vực có thể cuộn (có thể KHÔNG phải những gì OP muốn). Thay vào đó, hãy xem stackoverflow.com/a/39563193/1526986 .
xixixao

20

Đây là giải pháp đơn giản nhất mà tôi có thể tập hợp:

 <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
Khi tôi sử dụng câu trả lời này hoặc các câu trả lời tương tự khác, nó làm cho các mục trong danh sách biến mất (có vẻ như nó đang cuộn quá xa, nhưng tôi không thể chắc chắn). Việc cuộn một chút sẽ khiến mọi thứ xuất hiện trở lại và giống như nó đang trượt trở lại chế độ xem (do đó, lý thuyết cuộn quá xa). Bất kỳ ý tưởng rõ ràng tại sao điều đó có thể là?
tscizzle

11

Đối với bất kỳ ai viết thành phần hàm có thể sử dụng hook,

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

thử giải pháp này

xcode 10.0

RN: 56.0.0

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

tại sao ref=? có vẻ giống như một di tích của nhà phát triển web
YungGun

5

Trong trường hợp bất kỳ ai trong năm 2020 hoặc sau đó đang tự hỏi làm thế nào để đạt được điều này với các thành phần chức năng. Đây là cách tôi đã làm:

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

Lưu ý: bạn có thể đề cập rằng bạn đang sử dụng useref. hầu hết ppl không biết cách sử dụng ref với hooks.
ʎzɐɹƆ

Đúng. Vấn đề là nó hoạt động hiệu quả ngay cả khi không sử dụng hook useRef. Nhưng đúng cách là sử dụng nó! Bạn nói đúng
Marlon Englemam

4

Bạn có thể đảo ngược chế độ xem cuộn / danh sách bằng cách sử dụng react-native-invertible-scroll-viewmô-đun , sau đó chỉ cần gọi ref.scrollTo(0)để cuộn xuống dưới cùng.

Cài đặt mô-đun:

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

Sau đó nhập thành phần:

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

Sau đó, sử dụng JSX sau thay vì ScrollView:

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

Điều này hoạt động vì react-native-invertible-scroll-viewlật toàn bộ nội dung của chế độ xem. Lưu ý rằng con của chế độ xem cũng sẽ hiển thị theo thứ tự ngược lại với chế độ xem bình thường - con đầu tiên sẽ xuất hiện ở dưới cùng .


3

Trên thực tế, câu trả lời @Aniruddha không phù hợp với tôi vì tôi đang sử dụng "react-native": "0.59.8"this.scrollView.root.scrollToEndcũng không được xác định

nhưng tôi đã tìm ra và điều này hoạt động this.scrollView.scrollResponderScrollToEnd({animated: true});

Nếu bạn đang sử dụng, 0.59.8điều này sẽ hoạt động HOẶC nếu bạn đang sử dụng phiên bản mới hơn và điều này phù hợp với bạn. vui lòng thêm các phiên bản trong câu trả lời này để những người khác không gặp rắc rối.

Mã đầy đủ tại đây

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

1

Phương pháp này hơi hack một chút nhưng tôi không thể tìm ra cách tốt hơn

  1. Đầu tiên hãy thêm một giới thiệu vào ScrollView của bạn.
  2. Thứ hai, thêm một Chế độ xem giả trước khi đóng thẻ ScrollView của bạn
  3. Nhận vị trí y của Chế độ xem giả đó
  4. Bất cứ khi nào bạn muốn cuộn xuống dưới cùng, hãy cuộn đến vị trí của Chế độ xem giả

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>
    );
  }


Đây không phải là khá gì được yêu cầu; cái này sẽ cuộn xuống cuối khi nhấn, thay vì giữ cho chế độ xem cuộn được cuộn xuống dưới cùng khi nội dung được thêm vào. Dù sao đi nữa, những scrollToBottomcách tiếp cận như thế này đã trở nên lỗi thời trong các phiên bản React Native mới hơn.
Mark Amery

1

Điều này trả lời câu hỏi của bạn: https://stackoverflow.com/a/39563100/1917250

Bạn cần phải liên tục cuộn đến cuối trang bằng cách tính chiều cao của nội dung trừ đi chiều cao của scrollView.


Đây là câu trả lời chính xác cho câu hỏi. Bạn có thể thông minh về thời điểm thực hiện cuộn, câu trả lời trong liên kết chỉ cho bạn cách lấy những con số bạn cần. Đặc biệt, nếu bạn đang thêm các thành phần vào danh sách, chiều cao nội dung sẽ không bao gồm chúng khi componentDidUpdate được gọi, vì vậy bạn cần nhớ rằng bạn muốn cuộn và cuộn trên onContentSizeChange thay thế.
xixixao

1

Nếu bạn sử dụng TypeScript Bạn cần thực hiện việc này

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

Tôi đã đấu tranh với điều này trong một thời gian và cuối cùng đã tìm ra thứ gì đó hoạt động thực sự hiệu quả và suôn sẻ cho tôi. Giống như nhiều người, tôi đã cố gắng .scrollToEndnhưng không có kết quả. Các giải pháp mà làm việc cho tôi là sự kết hợp của onContentSizeChange, onLayoutđạo cụ và hơn một chút suy nghĩ trực quan về "những gì di chuyển xuống đáy" thực sự có nghĩa. Phần cuối cùng có vẻ tầm thường đối với tôi bây giờ, nhưng tôi đã mất mãi mãi để tìm ra.

Giả sử rằng ScrollViewcó chiều cao cố định, khi chiều cao nội dung vượt quá chiều cao của vùng chứa, tôi đã tính toán phần bù bằng cách trừ prevHeight(chiều cao cố định của ScrollView) cho currHeight(chiều cao nội dung). Nếu bạn có thể hình dung trực quan nó, hãy cuộn đến sự khác biệt đó trong trục y, trượt chiều cao nội dung trên vùng chứa của nó bằng khoảng chênh lệch đó. Tôi đã tăng độ lệch của mình và đặt chiều cao nội dung hiện tại của tôi bằng chiều cao trước đó của tôi và tiếp tục tính toán bù mới.

Đây là mã. Hy vọng nó sẽ giúp một ai đó.

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

Tôi đoán là đã quá muộn để trả lời nhưng tôi đã nhận được một lần hack! Nếu bạn sử dụng FlatListbạn có thể bật invertedbất động sản rơi trở lại thiết lập transform scaleđể -1(đó là chấp nhận được cho các thành phần khác như danh sách ScrollView...). Khi bạn kết xuất FlatListdữ liệu của mình, nó sẽ luôn ở dưới cùng!


-1

Hãy thử đặt thuộc tính contentOffset của dạng xem cuộn thành chiều cao hiện tại của nội dung.

Để hoàn thành những gì bạn cần, bạn có thể thực hiện việc này như một phần của sự kiện onScroll. Tuy nhiên, điều này có thể gây ra trải nghiệm người dùng không tốt vì vậy việc chỉ làm điều này có thể thân thiện hơn với người dùng khi có thêm thư mới.


-1; nope. Giống như nhiều câu trả lời ở đây, điều này có tác dụng cuộn ScrollViewxuống bên dưới tất cả nội dung của nó, thay vì chỉ cuộn xuống cuối.
Mark Amery

-1

Tôi đã gặp vấn đề tương tự, nhưng sau khi đọc trang này (khiến tôi đau đầu!) Tôi thấy gói npm rất đẹp này chỉ đảo ngược ListView và làm cho mọi thứ trở nên dễ dàng cho một ứng dụng trò chuyện !!


-1; điều này trùng lặp với một câu trả lời đã đăng trước đây ở đây (và cũng hơi nhầm lẫn; câu hỏi này là về a ScrollView, không phải về `ListView`).
Mark Amery

-3

Hơi muộn ... nhưng hãy xem câu hỏi này. Cuộn lên đầu ScrollView

ScrollView có phương thức "scrollTo".


1
Có, có một phương thức scrollTo trên ScrollView, nhưng câu hỏi cụ thể là hỏi về cách cuộn xuống dưới cùng, điều này không đơn giản.
Southerneer
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.