在React Native中实现双向无限滚动视图

4
我希望实现ScrollView的无限滚动,可以向两个方向滚动并且数据应该是动态加载的。
我正在使用SectionList组件进行列表操作。我已经实现了正向无限滚动,也就是说,如果用户向下滚动,数据将自动附加到列表中。
为此,我使用了onMomentumScrollEnd事件。当用户停止滚动时,如果滚动方向向上,则在结尾处追加数据;如果滚动方向向下,则在顶部追加数据。
现在的问题是:当我在列表顶部添加数据时,它会使当前列表数据全部向后移动。即使更新数据,我也不想移动当前列表。有没有办法解决这个问题?
以下是我的代码:

import React, {Component} from 'react';
import {
    Text,
    View,
    StyleSheet,
    SectionList,
} from 'react-native';
import CardComponent from './CardComponent'

export default class Schedule extends Component {
    constructor(props) {
        super(props);
        this.state = {
          sectionData: [],
          loading: false,
        }
        this.contentOffsetY = 0;
        this._onScroll = this._onScroll.bind(this)
    }
    
    componentDidMount() {
        this.setState({ sectionData: this.props.data })
    }
    
    renderItem = ({item}) => (
        <CardComponent
            data={item}
            key={item}
        />
    );
    
    renderDateSeparator(text) {
        return (
            <Text style={{
                    paddingVertical: 15,
                    fontSize: 14,
                    flex: 1,
                    textAlign: 'center',
                    textAlignVertical: 'center',
                }}>
               {text}
            <Text>
        )
    }
    
    _onScroll(e){
        let contentOffset = e.nativeEvent.contentOffset.y;
        this.contentOffsetY < contentOffset ? this.loadMoreOnBottom() : this.loadMoreOnTop();
        this.contentOffsetY = contentOffset;
    }
    
    loadMoreOnTop() {
      this.setState({ lodaing: true });
      // code to append data on top of list
      this.setState({ lodaing: false });
    }
    
    loadMoreOnBottom() {
      // code to append data at bottom of list
    }
    
    render() {
      const sectionData = this.state.sectionData;
      return(
        <View style={{flex: 1}}>
          <SectionList
                onMomentumScrollEnd={this._onScroll}
                automaticallyAdjustContentInsets={false}
                itemShouldUpdate={false}
                renderItem={this.renderItem}
                renderSectionHeader={({section}) => this.renderDateSeparator(section.date)}
                sections={sectionData}
                stickySectionHeadersEnabled={false}
                refreshing={this.state.loading}
                onRefresh={() => this.loadMoreOnTop()}
                onEndReachedThreshold={0.3}
                onEndReached={() => this.loadMoreOnBottom()}
                keyExtractor={(item) => item.key}
            />
        </View>
      )
    }
}

提前感谢。

1个回答

2
经过大量的研究,我终于在react-native上实现了双向无限滚动视图。
为了实现这个功能,我用FlatList替换了我的SectionList,因为我想要使用scrollToOffset方法,而SectionList中并没有完全支持。
我使用了JavaScript的setInterval函数。它定期检查列表是否需要从顶部或底部追加。
这是我的代码:

import React, {Component} from 'react';
import {
    Text,
    View,
    StyleSheet,
    FlatList,
    Dimensions,
} from 'react-native';
import CardComponent from './CardComponent'

let {height, width} = Dimensions.get('window');

export default class Schedule extends Component {
    constructor(props) {
        super(props);
        this.state = {
          listData: [],
        }
        this.contentOffsetY = 0;
        this.pageOffsetY = 0;
        this.contentHeight = 0;
        
        this._onScroll = this._onScroll.bind(this);
        this.loadMoreOnTop = this.loadMoreOnTop.bind(this);
        this.loadMoreOnBottom = this.loadMoreOnBottom.bind(this);
    }
    
    componentDidMount() {
        this.setState({ listData: this.props.data });
        this._interval = setInterval(() => {
            this.setState({ load: true });
        }, 2000);
    }

    componentWillUnmount() {
        clearInterval(this._interval);
    }

    renderItem = ({item}) => (
        <CardComponent
            data={item}
            key={item}
        />
    );
    
    _onScroll(e){
        let contentOffset = e.nativeEvent.contentOffset.y;
        this.contentOffsetY < contentOffset ? this.loadMoreOnBottom() : this.loadMoreOnTop();
        this.contentOffsetY = contentOffset;
    }
        
    scrollToOffset = (offset) => {
        this.flatListRef ? this.flatListRef.scrollToOffset({animated: false, offset}) : null;
    };

    loadMoreOnTop() {
      let newOffset;
      
      // code to append data on top of list
      
      // calculate newOffset:
      newOffset = this.pageOffsetY + space required for new data.

      this.contentOffsetY = newOffset;
      this.scrollToOffset(newOffset);
    }
    
    loadMoreOnBottom() {
      // code to append data at bottom of list
    }
    
    render() {
      const listData = this.state.listData;
      
      if(this.pageOffsetY < 600) {
          this.loadMoreOnTop();
      } else if((this.contentHeight - this.pageOffsetY) < (height * 1.5)){
          this.loadMoreOnBottom();
      }
      return(
        <View style={{flex: 1}}>
          <FlatList
                onScroll={(e) => {
                    this.pageOffsetY = e.nativeEvent.contentOffset.y;
                    this.contentHeight = e.nativeEvent.contentSize.height;
                    return null;
                }}
                onMomentumScrollEnd={this._onScroll}
                automaticallyAdjustContentInsets={false}
                itemShouldUpdate={false}
                renderItem={this.renderItem}
                data={listData}
                refreshing={false}
                onRefresh={() => this.loadMoreOnTop()}
                onEndReachedThreshold={0.3}
                onEndReached={() => this.loadMoreOnBottom()}
                keyExtractor={(item) => item.key}
                ref={(ref) => { this.flatListRef = ref; }}
                animated={false}
            />
        </View>
      )
    }
}


1
嘿,我正在和你建立完全相同的列表,但我想知道你如何计算“新数据所需的空间”?你的行高是固定的吗?因为我的每个项目的行高不同,所以很难知道在加载一堆行后我获得了多少高度。 - Marson Mao
是的,我已经为每一行指定了固定空间。但是如果您拥有动态行高,则需要根据接收到的新数据计算高度。计算所需空间对于平滑滚动体验至关重要。 - Nidhi Shah
1
谢谢您快速回复!您有计算动态高度行的经验吗?或者您有任何提示吗?我能想到的是在行安装后收集它们的高度,但这似乎很麻烦,我想知道是否有更好的方法? - Marson Mao
@MarsonMao 有没有关于这个动态高度行的解决方法或变通之道?在2021年仍然存在这个问题。 - SungHo Choi
@SungHoChoi 对不起,我已经很长时间没有实现这个了,已经失去了所有的记忆。祝你好运! - Marson Mao
@MarsonMao 没问题!谢谢你的回复。 - SungHo Choi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接