React Native开发经验集React Native开发

低版本React Native中ListView和ScrollV

2018-10-16  本文已影响156人  恐怕是小珠桃子

我们日常开发的时候,经常会遇到上拉加载更多的需求,原因很简单,数据量大的时候分批次加载可提高加载效率,为用户省点流量和时间。

最近在使用RN0.35实现上拉加载时,遇到了一些坑,总结之后决定分享给大家。希望能给正在使用低版本RN的你提供一些帮助。

ListView实现上拉加载

1. 实现简单的上拉加载

ListView中提供了onEndReached方法,触发时机为当所有行都已渲染且列表已滚动到底部。如此说来,在ListView中实现上拉加载直接使用onEndReached就可以完成,如下(在loadMoreData中完成上拉加载功能):

<ListView onEndReached={this.loadMoreData}/>
2. 坑来了【划重点】

上述过程已经实现了上拉加载,但你会发现存在一个问题:当列表内容小于列表高度时,onEndReached被频繁触发,为什么呢?我们来一起看下RN的源码(ListView.js中的_getDistanceFromEnd_maybeCallOnEndReached方法):

_maybeCallOnEndReached: function(event?: Object) {
  if(... // 省略
    && this._getDistanceFromEnd(this.scrollProperties) < this.props.onEndReachedThreshold
    ... // 省略) {
    this.props.onEndReached(event);
  }
},

_getDistanceFromEnd:function _getDistanceFromEnd(scrollProperties){
  return scrollProperties.contentLength - scrollProperties.visibleLength - scrollProperties.offset;
},

从上述代码中,我们提取到,onEndReached被触发的重要条件是:

contentLength - visibleLength - offset < onEndReachedThreshold

其中contentLength是列表内容的长度,visibleLength是列表的高,offset是滑动的距离,onEndReachedThreshold是默认值为1000的可控常量。回到之前的问题,当列表内容小于列表高度时(contentLength < visibleLength),offset一定是0(因为这会儿滑不动。。)。也就是此时说onEndReached被触发的条件是:

那么onEndReachedThreshold的含义是什么?可以设置为负数吗?
答案是当然不行!设置onEndReachedThreshold的原因是在我们实现上拉加载的时候,如果到了底部再加载,用户看起来会比较慢,设置onEndReachedThreshold可以使得onEndReached提前触发,(距离列表底部onEndReachedThresholdpixel时触发。可自行画图理解其中关系

到这你可能已经明白了,我们只需要控制仅当内容大于或等于列表高度时(contentLength >= visibleLength),触发上拉加载,如下:

handleEndReached = () => { 
  const { contentLength, visibleLength } = this.listView.scrollProperties;
    if (contentLength >= visibleLength) {
      this.loadMoreData();
    }
}

<ListView
  ref={(component) => { this.listView = component; }}
  onEndReached={this.handleEndReached}
  onEndReachedThreshold={20}
/>

ScrollView实现上拉加载

细心的你一定已经发现,ScrollView并没有提供onEndReached方法,因为ScrollView是不会限制长度的,内容是无限长的。如果因为业务原因必须使用ScrollView,又需要上拉加载,怎么办呢?

ScrollView提供了onScrollEndDrag方法,onScrollEndDrag会在每次结束滚动后触发,我们要做的就是,在这个时机中,判断是否滑动到了底部,如果到了底部,则上拉加载。方法和ListView类似,我们依然可以找到内容高度,列表高度,以及滑动的距离来判断是否需要加载。如下:

handleScrollEnd = (event) => {
    const contentHeight = event.nativeEvent.contentSize.height;
    const scrollViewHeight = event.nativeEvent.layoutMeasurement.height;
    const scrollOffset = event.nativeEvent.contentOffset.y;

    const isEndReached = scrollOffset + scrollViewHeight >= contentHeight; // 是否滑动到底部
    const isContentFillPage = contentHeight >= scrollViewHeight; // 内容高度是否大于列表高度

    if (isContentFillPage && isEndReached) {
      this.loadMoreData();
    }
  };
  
<ScrollView
    onScrollEndDrag={this.handleScrollEnd}
/>

如果你有别的问题或对本文存在疑问,评论区等你哈

上一篇下一篇

猜你喜欢

热点阅读