constructor(props) { super(props); this.state = { isHeaderRefreshing: false, // 头部是否正在刷新 isFooterRefreshing: false, // 尾部是否正在刷新 footerState: RefreshState.Idle, // 尾部当前的状态,默认为Idle,不显示控件 } }
render函数如下:
render() { return ( <FlatList {...this.props} onRefresh={()=>{ this.beginHeaderRefresh() }} refreshing={this.state.isHeaderRefreshing} onEndReached={() => { this.beginFooterRefresh() }} onEndReachedThreshold={0.1} // 这里取值0.1(0~1之间不包括0和1),可以根据实际情况调整,取值尽量小 ListFooterComponent={this._renderFooter} /> ) } _renderFooter = () => { return ( <RefreshFooter state={this.state.footerState} onRetryLoading={()=>{ this.beginFooterRefresh() }} /> ) };
可以看到上面的代码中有beginHeaderRefresh和beginFooterRefresh两个方法,这两个方法就是用来调用刷新的,但是在刷新之前还有一些逻辑情况需要判断。比如头部和尾部不能够同时刷新,不然数据处理结果可能受到影响,正在刷新时要防止重复的刷新操作,这些都是要考虑的。这里我在代码中详细注释了:
/// 开始下拉刷新 beginHeaderRefresh() { if (this.shouldStartHeaderRefreshing()) { this.startHeaderRefreshing(); } } /// 开始上拉加载更多 beginFooterRefresh() { if (this.shouldStartFooterRefreshing()) { this.startFooterRefreshing(); } } /*** * 当前是否可以进行下拉刷新 * @returns {boolean} * * 如果列表尾部正在执行上拉加载,就返回false * 如果列表头部已经在刷新中了,就返回false */ shouldStartHeaderRefreshing() { if (this.state.footerState === RefreshState.refreshing || this.state.isHeaderRefreshing || this.state.isFooterRefreshing) { return false; } return true; } /*** * 当前是否可以进行上拉加载更多 * @returns {boolean} * * 如果底部已经在刷新,返回false * 如果底部状态是没有更多数据了,返回false * 如果头部在刷新,则返回false * 如果列表数据为空,则返回false(初始状态下列表是空的,这时候肯定不需要上拉加载更多,而应该执行下拉刷新) */ shouldStartFooterRefreshing() { if (this.state.footerState === RefreshState.refreshing || this.state.footerState === RefreshState.NoMoreData || this.props.data.length === 0 || this.state.isHeaderRefreshing || this.state.isFooterRefreshing) { return false; } return true; }
其中startHeaderRefreshing和startFooterRefreshing的逻辑如下:
/// 下拉刷新,设置完刷新状态后再调用刷新方法,使页面上可以显示出加载中的UI,注意这里setState写法 startHeaderRefreshing() { this.setState( { isHeaderRefreshing: true }, () => { this.props.onHeaderRefresh && this.props.onHeaderRefresh(); } ); } /// 上拉加载更多,将底部刷新状态改为正在刷新,然后调用刷新方法,页面上可以显示出加载中的UI,注意这里setState写法 startFooterRefreshing() { this.setState( { footerState: RefreshState.Refreshing, isFooterRefreshing: true }, () => { this.props.onFooterRefresh && this.props.onFooterRefresh(); } ); }
在刷新之前,我们需要将头部或尾部的组件显示出来,然后再调用外部的数据接口方法。这里setState这样写的好处是state中的值更新完成后才会调用箭头函数中的方法,是有严格顺序的,如果把 this.props.onFooterRefresh && this.props.onFooterRefresh() 写在setState外部,在UI上我们可能看不到头部的loading或者尾部的努力加载中,接口方法就已经调用完毕了。
最后,在刷新结束后我们还需要调用停止刷新的方法,使头部或尾部组件不再显示,否则一直是加载中还可能让人以为是bug。下面看看停止刷新的方法:
/** * 根据尾部组件状态来停止刷新 * @param footerState * * 如果刷新完成,当前列表数据源是空的,就不显示尾部组件了。 * 这里这样做是因为通常列表无数据时,我们会显示一个空白页,如果再显示尾部组件如"没有更多数据了"就显得很多余 */ endRefreshing(footerState: RefreshState) { let footerRefreshState = footerState; if (this.props.data.length === 0) { footerRefreshState = RefreshState.Idle; } this.setState({ footerState: footerRefreshState, isHeaderRefreshing: false, isFooterRefreshing: false }) }
这里传入一个尾部组件状态的参数是为了更新尾部组件的样式。同时对数据源data进行一个判断,如果为空说明当前没有数据,可以显示空白页面,那么尾部组件也没必要显示了。
以下是我使用RefreshListView实现的豆瓣电影页面分页加载的效果图: