React-Native中使用ListView时一般都会用到下拉刷新和上拉加载更多功能,系统提供有RefreshControl
下拉刷新组件,简单易用。
在做上拉加载更多功能时却比较麻烦,在仔细研究了ReactNative官网文档之后,依然没有找到实现办法,但是可以了解到上拉加载功能肯定离不开ListView的两个属性onEndReached
、onEndReachedThreshold
。
官网文档中同时提到了
renderFooter
页脚渲染属性,网上搜到的很多也用到了这个属性,但是我在使用过程中发现它对我并没有帮助
onEndReached
当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的),这个事件也会被触发,请自行做标记过滤。
onEndReachedThreshold
调用onEndReached之前的临界值,单位是像素。
下面记录我在实现上拉加载功能的具体方法和代码。首先自定义正在加载更多和已加载全部的组件,新建LoadMoreFooter.js
文件,复制粘贴以下代码。
import React, { Component } from 'react';
import {
View,
Text,
StyleSheet,
ActivityIndicator
} from 'react-native';
var LoadMoreFooter = React.createClass ({
getDefaultProps(){
return{
isLoadAll:false
}
},
render() {
return (
<View style={styles.footer}>
<ActivityIndicator animating={!this.props.isLoadAll} />
<Text style={styles.footerTitle}>{this.props.isLoadAll ? '已加载全部' : '正在加载更多…'}</Text>
</View>
)
}
})
var styles = StyleSheet.create({
footer: {
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
height: 30,
},
footerTitle: {
marginLeft: 10,
fontSize: 13,
color: 'gray'
}
})
module.exports = LoadMoreFooter;
接下来要处理自定义加载更多组件的显示与隐藏逻辑。当this.state.isLoadMore ==true
时显示<LoadMoreFooter />
组件,否则为null
render() {
return (
this.state.isEmptyData ?
this.isEmptyData() :
<View style={styles.container}>
{ /*列表*/ }
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
onEndReached={this._toEnd}
onEndReachedThreshold={10}
// renderFooter={this._toEnd}
refreshControl={
<RefreshControl
refreshing={this.state.isRefreshing}
tintColor="gray"
title="正在刷新"
onRefresh={this._onRefresh}/>}
/>
{
this.state.isLoadMore ?
<LoadMoreFooter isLoadAll={!this.state.isLoadMore} />
: null
}
</View>
);
},
下面要处理的是加载数据时最复杂的逻辑关系:刷新当前数据、还是加载更多数据。这里定义了this.state.isRefreshing
属性来区分是否正在刷新当前数据。同时定义了this.state.data
属性,当正在加载更多数据时,需要在当前页列表dataSource的基础上追加下一页的数据,即[...this.state.data , ...JSON.parse(responseData).data]
。
向动态数组中添加对象,使用的是方法是
[...array_one , ...array_two]
,与OC中的可变数组不同。
getInitialState(){
//创建数据源
var ds = new ListView.DataSource({rowHasChanged:(row1, row2) => row1 !== row2});
//初始化数据源
return {
data:null,
dataSource: ds,
isEmptyData:false,
isRefreshing:true,
extend:'',
isLoadMore:false,
page:1
}
},
//当列表滑动到最后一个Cell时,调用此方法
_toEnd(){
if (this.state.isRefreshing==false && this.state.data.length>=_pageSize) {
this.setState({
isLoadMore:true,
})
// 立即更改属性值 page+1
this.state.page = this.state.page + 1
// 网络请求数据
this.getEduData();
}
},
注意事项:
react native setState之后的state值不能立即使用,setState之后,需要走完RN生命周期,也就是走到render时,state的值才会变成setState的值,要立即使用state的值,需要直接更改,也即this.state.something = 'now';
所以在设置page页码加1时,不能直接在setState中设置,要实现page属性立刻加1,使用this.state.page = this.state.page + 1
数据请求成功后,要在请求成功的回调方法中更新数据源。使用三木运算来分别更新刷新页面数据、和加载更多数据时的业务逻辑。
// 更新数据源
var data = this.state.data;
if (!JSON.parse(responseData).data || JSON.parse(responseData).data.length==0) {
this.setState({
isEmptyData: this.state.isLoadMore ? false : true,
isRefreshing:false,
isLoadMore:false
});
if (this.state.isLoadMore) {
// 立即更改属性值 page-1
this.state.page = this.state.page - 1
}
} else {
data = this.state.isLoadMore ? [...this.state.data , ...JSON.parse(responseData).data] : JSON.parse(responseData).data
this.setState({
data:data,
isRefreshing:false,
dataSource:this.state.dataSource.cloneWithRows(data),
isLoadMore:false,
});
}
如果请求的数据为空,展示给用户提示信息。
isEmptyData(){
return (
<ScrollView style={{backgroundColor:'#e8e8e8'}}>
<View style={styles.emptyDataStyle}>
<Image source={{uri:'bv_dropbox'}} style={styles.emptyDataIcon}/>
<Text style={{marginTop:5,color:'gray'}}>暂未有资讯</Text>
</View>
</ScrollView>
)
}
网友评论