ListView是android中使用频率非常高的控件,在React-Native中也是如此。
翻阅了关于ListView的一些介绍和博客,我自己也总结了一下。
使用ListView的步骤,最开始是初始化ListView.DataSource,在构造它时指定更新策略。有两种策略,分别为rowHasChanged和sectionHeaderHasChanged。
顾名思义就是表示行变化和sectionHeader变化时行,就会进行更新。
我简单的理解:
如果你的ListView数据源是纯数组,那么不需要设置sectionHeaderHasChanged,如果数据源有是键值对,需要定义。
另外就是cloneWithRows和cloneWithRowsAndSections,为数据源赋予数据。
另外注意的是,如果你之前的dataSource有值,然后进行clone操作,那么会覆盖之前的值,clone操作会重新生成一个数据源。
经过对ListView的简单研究,我写了一个上拉刷新,加载更多的ListView的例子。源码如下:
import ListViewItemDemo from './ListViewItemDemo';
import Toast,{DURATION} from 'react-native-easy-toast';
const URL="http://101.201.78.24/api//activity/list";
let pn=1;
let maxResults=10;
const PageSize="&maxResults="+maxResults;
export default class ListViewDemo extends Component {
constructor(props){
super(props);
this.state={
count:0,
result:'',
refreshing:true,
dataSource:new ListView.DataSource({
rowHasChanged:(r1,r2)=>r1!=r2,
}),
isFirstIn:true,
dataArray:[],
};
}
getPromise(url){
var promise=new Promise((resolve,reject)=>{
fetch(url).then(response=> response.json()).
then(result=>resolve(result)).
catch(error=>reject(error));
});
return promise;
}
/*
网络加载是异步的,需要结合Promise来进行回调。
fetch(URL)resolve返回的数据是response对象
response.json(),解析一个字符串,构建一个对象或值。相当于JSON.parse()
JSON.stringify,从一个对象中解析出字符串
*/
componentDidMount(){
this.setState({
refreshing:true,
});
let url=URL+"?pn="+pn+PageSize;
this.getPromise(url).then(result=> {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(result.data.results),
refreshing:false,
dataArray:result.data.results,
})
}
).catch(error=>{
console.log(error)
});
}
renderRow(item){
return <ListViewItemDemo item={item}/>
}
renderLine(){
return <View style={styles.line}></View>
}
onRefresh(){
this.state.dataArray=[];
pn=1;
let url=URL+"?pn="+pn+PageSize;
this.getPromise(url).then(result=> {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(result.data.results),
refreshing:false,
dataArray:result.data.results,
})
}
).catch(error=>{
console.log(error)
});
}
onEndReached(){
//无论onEndReachedThreshold为多少,首次进来页面,都会调用onEndReached。第一次请求数据时,不加载数据
// this.refs.toast.show("onEndReached0",DURATION.LENGTH_SHORT);
if(this.state.isFirstIn){
this.setState({
isFirstIn:false,
});
return;
}
this.refs.toast.show("正在加载更多",DURATION.LENGTH_SHORT);
pn++;
let url=URL+"?pn="+pn+PageSize;
this.getPromise(url).then(result=> {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(this.state.dataArray.concat(result.data.results)),
refreshing:false,
isFirstIn:false,
dataArray:this.state.dataArray.concat(result.data.results),
})
}
).catch(error=>{
console.log(error)
});
}
render(){
return(
<View style={styles.containers}>
<ListView
dataSource={this.state.dataSource}
renderRow={(item)=>this.renderRow(item)}
renderSeparator={()=>this.renderLine()}
//RefreshControl:给ListView添加下拉刷新
//refreshing,在刷新时是否显示显示器,
//onRefresh刷新时执行
refreshControl={
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={()=>this.onRefresh()}
/>
}
onEndReachedThreshold={100}
onEndReached={
()=>this.onEndReached()
}
/>
{/*Toast在根视图的底部去使用,在视图被渲染时,把toast声明*/}
<Toast
ref="toast"/>
</View>
)
}
}
const styles=StyleSheet.create({
containers: {
flex:1,
},
line:{
backgroundColor:'red',
height:0.5,
}
});
基本的过程就是,进入页面,显示下拉刷新控件,加载第一页的数据。
并且把第一页的数据添加到一个临时数组。
onEndReached,文档中这样说的,当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。
可是无论设置onEndReachedThreshold为多少值,onEndReached总会先调用。
而我也发现了一个规律,onEndReachedThreshold可以理解为一个item的高度,如果你设置的onEndReachedThreshold值大于item高度,当拉到底部时,会触发该方法。如果过小,则不会触发。
对于有的item高度不方便计算的,onEndReachedThreshold写一个较大值即可。
在onEndReached中,则是把请求数据concat到临时数组,再添加给数据源。
而onRefresh下拉刷新时,清除临时数组。请求第一页数据显示。
自此,一个简单的React-native ListView下拉刷新加载更多的例子完成了。
为了方便调试,还使用Toast组件。
ListView的item的代码如下:
const ImageHost='http://101.201.78.24';
export default class ListViewItemDemo extends Component {
constructor(props){
super(props);
}
getActType(){
var temp="";
switch (this.props.item.type){
case 0:
temp="全部";
break;
case 1:
temp="课程";
break;
case 2:
temp="峰会";
break;
case 3:
temp="路演";
break;
case 4:
temp="沙龙";
break;
case 5:
temp="其他";
break;
}
return temp;
}
render(){
return(
<View style={styles.item}>
<View style={styles.leftSection}>
//默认的图片(图片加载不出来显示)
<Image
resizeMode='cover'
style={{width:120,height:80,position:'absolute'}}
source={require('../img/default_activity_list.jpg')}/>
<Image
resizeMode='cover'
style={{width:120,height:80,position:'absolute'}}
source={{uri:ImageHost+this.props.item.thumbnail}}/>
</View>
<View style={styles.rightSection}>
<Text style={{fontSize:18}}>{this.props.item.title}</Text>
<View style={{
marginVertical:10,
flexDirection:'row',
}}>
<Text>{this.props.item.startTime}</Text>
<Text style={{marginLeft:10}}>{this.props.item.city}</Text>
</View>
<View style={{
marginVertical:10,
flexDirection:'row',
}}>
<Text>{this.getActType()}</Text>
</View>
</View>
</View>)
}
}
const styles=StyleSheet.create({
item: {
flex:1,
padding:15,
flexDirection:'row',
},
leftSection:{
position:'relative'
},
rightSection:{
marginLeft:145,
flexDirection:'column',
},
});