zl程序教程

您现在的位置是:首页 >  移动开发

当前栏目

《React-Native系列》19、 ListView组件之上拉刷新(iOS和Android通用)

Androidios组件React 系列 native 通用 刷新
2023-09-27 14:29:03 时间

ReactNative提供了RefreshControl下拉刷新组件,但是没有提供上拉刷新组件,上拉刷新在App中是很常用的。

今天我们来实现一个iOSAndroid通用的上拉刷新功能。

下面简要介绍下我实现的思路。

如果你对ListView的基础知识不是很清楚,建议先移步:《React-Native系列》16、 RN组件之ListView


思路:

1、常量定义:

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. const moreText = "加载完毕";    //foot显示的文案  
  2. //页码  
  3. var pageNum = 1;  
  4. //每页显示数据的条数  
  5. const pageSize = 10;  
  6. //页面总数据数  
  7. var pageCount = 0;  
  8. //页面List总数据  
  9. var totalList = new Array();  
  10.   
  11. //foot:  0 隐藏  1  已加载完成   2  显示加载中  

2、定义ListView

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. <ListView  
  2.     enableEmptySections={true}  
  3.     dataSource={this.state.dataSource}  
  4.     renderRow={this._renderRow.bind(this)}  
  5.     renderFooter={this._renderFooter.bind(this)}  
  6.     onEndReached={this._endReached.bind(this)}  
  7.     onEndReachedThreshold={0}  
  8. />  



3、声明State状态机变量

ListView.DataSource实例(列表依赖的数据源)

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. constructor(props) {  
  2.     super(props);  
  3.     this.state = {  
  4.         dataSource: new ListView.DataSource({  
  5.             rowHasChanged: (r1, r2) => r1 !== r2,  
  6.         }),  
  7.        loaded: false,//控制Request请求是否加载完毕  
  8.        foot:0,// 控制foot, 0:隐藏foot  1:已加载完成   2 :显示加载中  
  9.        error:false,  

这里我们主要声明了dataSource,这个没什么说的

loaded:用来控制整个页面的菊花

error:如果Request错误,显示一个错误页面

foot: 控制Footer的view


4、渲染页面前,加载数据

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. componentWillMount() {  
  2.     this._fetchListData();  
  3. }  

5、Load服务端数据

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. _fetchListData() {  
  2.     if(pageNum > 1){  
  3.       this.setState({loaded:true});  
  4.     }  
  5.     fetch(requestURL, {  
  6.         method: 'get',  
  7.         headers: headerObj,  
  8.     }).then(response =>{  
  9.       if (response.ok) {  
  10.           return response.json();  
  11.       } else {  
  12.           this.setState({error:true,loaded:true});  
  13.       }  
  14.     }).then(json=>{  
  15.         let responseCode = json.code;  
  16.         if (responseCode == 0) {  
  17.             let responseData = json.data;  
  18.   
  19.             pageCount = responseData.count;  
  20.             let list = responseData.data;  
  21.   
  22.             if (orderList == null) {  
  23.                 orderList = [];  
  24.                 currentCount = 0;  
  25.             } else {  
  26.                 currentCount = list.length;  
  27.             }  
  28.             if(currentCount < pageSize){  
  29.               //当当前返回的数据小于PageSize时,认为已加载完毕  
  30.               this.setState({ foot:1,moreText:moreText});  
  31.             }else{//设置foot 隐藏Footer   
  32.               this.setState({foot:0});  
  33.             }  
  34.             for (var i=0; i < list.length; i++) {  
  35.               totalList.push( list[i] );  
  36.             }  
  37.   
  38.             this.setState({  
  39.                 dataSource: this.state.dataSource.cloneWithRows(totalList),  
  40.                 loaded: true,  
  41.             });  
  42.         }else{  
  43.             this.setState({error:true,loaded:true});  
  44.         }  
  45.     }).catch(function (error) {  
  46.         this.setState({error:true,loaded:true});  
  47.     });  
  48. }  

这里的细节挺多的:

1、当pageNum > 1时,就不要整个页面的菊花,此时loaded一直为true,这个主要是为了页面效果,要不然没加载一页数据,这个屏幕就会闪一下。

2、比较当前返回的list的大小,是否小于pageSize,控制Footer是否隐藏,还是显示已加载完毕

3、声明了一个全局的totalList对象,每次有新数据的时候,都push进去。

如果不采用push的方式的话,直接采用setState方法的话,第二页会把第一页的数据覆盖掉。


6、定义renderRow方法

renderRow={this._renderRow.bind(this)}   列表组件渲染函数 ,此处页面逻辑省略。


7、定义renderFooter方法

renderFooter   页脚会在每次渲染过程中都重新渲染。

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. _renderFooter() {  
  2.     if(this.state.foot === 1){//加载完毕  
  3.       return (  
  4.         <View style={{height:40,alignItems:'center',justifyContent:'flex-start',}}>  
  5.             <Text style={{color:'#999999',fontSize:12,marginTop:10}}>  
  6.                 {this.state.moreText}  
  7.             </Text>  
  8.         </View>);  
  9.     }else if(this.state.foot === 2) {//加载中  
  10.       return (  
  11.         <View style={{height:40,alignItems:'center',justifyContent:'center',}}>  
  12.           <Image source={{uri:loadgif}} style={{width:20,height:20}}/>  
  13.         </View>);  
  14.     }  
  15. }  

根据状态机变量foot控制Footer的显示



8、onEndReached 定义

onEndReachedThreshold={0}

当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用。原生的滚动事件会被作为参数传递。译注:当第一次渲染时,如果数据不足一屏(比如初始值是空的),这个事件也会被触发

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. _endReached(){  
  2.   if(this.state.foot != 0 ){  
  3.     return ;  
  4.   }  
  5.   this.setState({  
  6.     foot:2,  
  7.   });  
  8.   this.timer = setTimeout(  
  9.     () => {  
  10.       pageNum ++;  
  11.       this._fetchListData();  
  12.     },500);  
  13. }  

这里需要注意一下几点

1、第一屏的时候可能也会触发_endReached方法,所以需要判断foot为非 0(即加载中和已加载完毕)时,直接return

2、上拉时,触发_endReached方法,可能server端接口响应很快,几乎看不到菊花效果,特地加了个500毫秒的等待 


9、卸载Timer

[javascript]  view plain  copy
  在CODE上查看代码片 派生到我的代码片
  1. componentWillUnmount() {  
  2. // 如果存在this.timer,则使用clearTimeout清空。  
  3. // 如果你使用多个timer,那么用多个变量,或者用个数组来保存引用,然后逐个clear  
  4.   this.timer && clearTimeout(this.timer);  
  5. }  


待优化点:

1、如果ListView的翻页较多的话,全局变量totalList会非常大,对内存是考验。


如果有问题疑问,可以留言交流。