美文网首页程序员大前端-BFE
react native基于FlatList下拉刷新上拉加载实现

react native基于FlatList下拉刷新上拉加载实现

作者: JsLin_ | 来源:发表于2018-09-29 20:20 被阅读22次

    官方介绍:https://reactnative.cn/docs/flatlist/

    react native 的上拉加载一直困扰着自己,一直用的第三方组件,但是可维护性不高,而且也不太好用,最近工作没那么忙,就研究下了官方的FlatList,做出来的成果,比第三方组件流畅度高好多,而且也很好用
    下面是效果图:


    ios效果图 android效果图

    总体思路就是:就是计算屏幕高度,然后减去导航的头部,根据列表高度计算出每页的个数,然后向上取整。这样做的目的是:防止不满屏状态下的,onEndReached函数的主动触发。
    方法实现:

     //满屏页面判断
      fullScreenJusting(ItemHeight) {
        const screnHeight = screnInfo.size.height;     //屏幕高度
        //计算列表个数
        const listNum = (screnHeight - 40) / ItemHeight;
        return Math.ceil(listNum);
      }
    

    下拉刷新用的是 RefreshControl
    官网地址:https://reactnative.cn/docs/refreshcontrol/#progressbackgroundcolor

    完整代码:

    import React, { Component } from 'react';
    import {
      View,
      Text,
      Image,
      StyleSheet,
      FlatList,
      RefreshControl,
      ActivityIndicator,
    } from 'react-native';
    import { SafeAreaView } from 'react-navigation';
    import screnInfo from '../utils/View';
    import BaseStyle from '../constants/Style';
    import { QUESTION_LIST } from '../constants/Api';
    import { form_req } from '../utils/Request';
    
    export default class TestScreen extends Component {
      constructor(props) {
        super(props);
        this.state = {
          data: [
          ],
          refreshing: false,
          fresh: true,
          animating: true,
          nomore: false,
          pageSize: 0,
          pageNumber: 1,
        };
      }
      componentDidMount() {  //初始化的时候要判断长度 控制上拉加载
    
        const ListNums = this.fullScreenJusting(50);
        this.setState({
          pageSize: ListNums
        })
        this.onEndReachedCalled = false;
         this.getOrderList(ListNums, 1, true);
    
      }
      //满屏页面判断
      fullScreenJusting(ItemHeight) {
        const screnHeight = screnInfo.size.height;     //屏幕高度
        //计算列表个数
        const listNum = (screnHeight - 40) / ItemHeight;
        return Math.ceil(listNum);
      }
    
      getOrderList(ListNums, pageNumber, fresh) {
        let nomore;
        form_req(QUESTION_LIST, {
          page: pageNumber,
          perpage: ListNums,
        }).then(res => {
          if (res.code == 200) {
            const item = res.data;
            if (item.length < ListNums) {
              nomore = true
            } else {
              nomore = false
            }
            if (fresh) {
              this.setState({
                data: item,
                nomore: nomore
              })
             
            } else {
              this.setState({
                data: this.state.data.concat(item),
                nomore: nomore,
              })
            }
            // this.onEndReachedCalledDuringMomentum = true;
    
          } else {
    
          }
        });
      }
    
      renderItem = item => {
        return (
          <View style={styles.item} key={item.id}>
            <Text>{item.name}</Text>
          </View>
        );
      };
      //列表线
      ItemSeparatorComponent = () => {
        return <View style={styles.baseLine} />;
      };
      //头部
      ListHeaderComponent = () => { };
      //尾部
      ListFooterComponent = () => {
        return (
          <View style={styles.bottomfoot}>
            {
              this.state.data.length != 0 ?
                this.state.nomore ? (
                  <Text style={styles.footText}>- 我是有底线的 -</Text>
                ) : (
                    <View style={styles.activeLoad}>
                      <ActivityIndicator size="small" animating={this.state.animating} />
                      <Text style={[styles.footText, styles.ml]}>加载更多...</Text>
                    </View>
                  )
                :
                null
            }
    
          </View>
        );
      };
      //为空时
      ListEmptyComponent() {
        return (
          <View style={styles.noListView}>
            {/* <Image
              style={styles.noListImage}
              source={require('../images/status/order_no_record.png')}
            /> */}
            <Text style={styles.NoListText}>暂无订单</Text>
          </View>
        );
      }
      _keyExtractor = (item,index) => item.id;
    
      _onEndReached = () => {
        if (!this.state.nomore && this.onEndReachedCalled ) {
          this.getOrderList(this.state.pageSize, ++this.state.pageNumber, false);
        }
        this.onEndReachedCalled=true;
    
      };
      _onRefresh() {
        this.setState({ nomore: false, pageNumber: 1 }, () => {
          this.getOrderList(this.state.pageSize, 1, true);
        })
    
      }
    
      render() {
        return (
          <SafeAreaView style={BaseStyle.flex}>
            <View style={styles.listConten}>
              <FlatList
                data={this.state.data}
                keyExtractor={this._keyExtractor}
                onEndReached={this._onEndReached}
                refreshing={true}
                renderItem={({ item }) => this.renderItem(item)}
                ItemSeparatorComponent={this.ItemSeparatorComponent}
                ListEmptyComponent={this.ListEmptyComponent}
                ListFooterComponent={this.ListFooterComponent}
                onEndReachedThreshold={0.1}
                refreshControl={
                  <RefreshControl
                    refreshing={this.state.refreshing}
                    colors={['#ff0000', '#00ff00', '#0000ff']}
                    progressBackgroundColor={"#ffffff"}
                    onRefresh={() => {
                      this._onRefresh();
                    }}
                  />
                }
              />
            </View>
          </SafeAreaView>
        );
      }
    }
    
    const styles = StyleSheet.create({
      listConten: {
        flex: 1,
        backgroundColor: '#ffffff',
      },
      item: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: "center",
        backgroundColor: '#ffffff',
        height: 50,
      },
      baseLine: {
        width: screnInfo.size.width,
        height: 1,
        backgroundColor: '#eeeeee',
      },
      noListView: {
        width: screnInfo.size.width,
        height: screnInfo.size.height - 140,
        justifyContent: 'center',
        alignItems: 'center',
      },
      NoListText: {
        marginTop: 15,
        fontSize: 18,
        color: '#999999',
      },
      noListImage: {
        width: 130,
        height: 140,
      },
      bottomfoot: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        padding: 10,
      },
      footText: {
        marginTop: 5,
        fontSize: 12,
        color: '#999999',
      },
    
      activeLoad: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
      },
      ml: {
        marginLeft: 10,
      },
    });
    
    

    这里的坑就是:当初始化进来页面的时候 上拉会主动触发,所以这里加了一个开关 this.onEndReachedCalled = false; 初始化给一个false 当触发了 设为true,放在调取接口之后

    这里对组件进行了封装,我们只需关注 页面渲染 和 下拉 上拉事件,其他交给组件,使用的时候也不用写一大推代码

    组件代码:

    'use strict';
    
    import React, { Component } from 'react';
    import {
      View,
      Text,
      StyleSheet,
      FlatList,
      RefreshControl,
      ActivityIndicator,
    } from 'react-native';
    
    import screnInfo from '../utils/View';
    import PropTypes from 'prop-types';
    
    /**
     * 使用方法  
     * 
     * 
     */
    
    export default class PullAndLoadScreen extends Component {
      static defaultProps = {
        data: [],
        ItemSeparatorComponent: () => {
          return <View style={styles.baseLine} />
        },
        ListEmptyComponent: () => {
          return (
            <View style={styles.noListView}>
              <Text style={styles.NoListText}>这里空空如也~</Text>
            </View>
          );
        },
        refreshing: false,
        animating: true,
        ItmeHeight: 50,
    
    
      };
      static propTypes = {
        data: PropTypes.array,
        keyExtractor: PropTypes.func,
        onEndReached: PropTypes.func,
        renderItem: PropTypes.func,
        ItemSeparatorComponent: PropTypes.func,
        ListEmptyComponent: PropTypes.func,
        ListFooterComponent: PropTypes.func,
        refreshing: PropTypes.bool,
        colors: PropTypes.array,
        progressBackgroundColor: PropTypes.string,
        onRefresh: PropTypes.func,
        animating: PropTypes.bool,
        nomore: PropTypes.bool,
        ItmeHeight: PropTypes.number,
    
      };
    
    
      constructor(props) {
        super(props);
      }
      _ListFooterComponent = () => {
        const { data, nomore, animating } = this.props;
        return (
          <View style={styles.bottomfoot}>
            {
              data.length != 0 ?
                nomore ? (
                  <Text style={styles.footText}>- 我是有底线的 -</Text>
                ) : (
                    <View style={styles.activeLoad}>
                      <ActivityIndicator size="small" animating={animating} />
                      <Text style={[styles.footText, styles.ml]}>加载更多...</Text>
                    </View>
                  )
                :
                null
            }
    
          </View>
        );
      };
      _renderItem = (item) => {
        return  this.props.renderItem(item);
      }
    
      render() {
        const {
          data,
          keyExtractor,
          onEndReached,
          ItemSeparatorComponent,
          ListEmptyComponent,
          refreshing,
          colors,
          progressBackgroundColor,
          onRefresh,
    
        } = this.props;
        return (
       
            <FlatList
              data={data}
              keyExtractor={keyExtractor}
              onEndReached={onEndReached}
              refreshing={true}
              renderItem={({ item }) => this._renderItem(item)}
              ItemSeparatorComponent={ItemSeparatorComponent}
              ListEmptyComponent={ListEmptyComponent}
              ListFooterComponent={this._ListFooterComponent}
              onEndReachedThreshold={0.1}
              refreshControl={
                <RefreshControl
                  refreshing={refreshing}
                  colors={colors}
                  progressBackgroundColor={progressBackgroundColor}
                  onRefresh={onRefresh}
                />
              }
            />
       
        );
      }
    }
    
    const styles = StyleSheet.create({
      baseLine: {
        width: screnInfo.size.width,
        height: 1,
        backgroundColor: '#eeeeee',
      },
      noListView: {
        width: screnInfo.size.width,
        height: screnInfo.size.height - 140,
        justifyContent: 'center',
        alignItems: 'center',
      },
      NoListText: {
        marginTop: 15,
        fontSize: 18,
        color: '#999999',
      },
      bottomfoot: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        padding: 10,
      },
      footText: {
        marginTop: 5,
        fontSize: 12,
        color: '#999999',
      },
      activeLoad: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
      },
      ml: {
        marginLeft: 10,
      },
    });
    
    

    使用

    /* eslint-disable */
    import React, { Component } from 'react';
    import {
      View,
      Text,
      Image,
      StyleSheet,
    } from 'react-native';
    import { SafeAreaView } from 'react-navigation';
    import screnInfo from '../utils/View';
    import BaseStyle from '../constants/Style';
    import { QUESTION_LIST } from '../constants/Api';
    import { form_req } from '../utils/Request';
    import Pull_Load from "../components/pull_load";
    
    export default class PullAndLoadScreen extends Component {
      constructor(props) {
        super(props);
        this.state = {
          data: [
          ],
          nomore: false,
          pageSize: 0,
          pageNumber: 1,
        };
      }
      componentDidMount() {  //初始化的时候要判断长度 控制上拉加载
    
        const ListNums = this.fullScreenJusting(50);
        this.setState({
          pageSize: ListNums
        })
        this.onEndReachedCalled = false;
        this.getOrderList(ListNums, 1, true);
    
      }
      //满屏页面判断
      fullScreenJusting(ItemHeight) {
        const screnHeight = screnInfo.size.height;     //屏幕高度
        //计算列表个数
        const listNum = (screnHeight - 40) / ItemHeight;
        return Math.ceil(listNum);
      }
    
      getOrderList(ListNums, pageNumber, fresh) {
        let nomore;
        form_req(QUESTION_LIST, {
          page: pageNumber,
          perpage: ListNums,
        }).then(res => {
          if (res.code == 200) {
            const item = res.data;
            if (item.length < ListNums) {
              nomore = true
            } else {
              nomore = false
            }
            if (fresh) {
              this.setState({
                data: item,
                nomore: nomore
              })
    
            } else {
              this.setState({
                data: this.state.data.concat(item),
                nomore: nomore,
              })
            }
    
          } else {
    
          }
        });
      }
    
      _onEndReached = () => {
        if (!this.state.nomore && this.onEndReachedCalled) {
          this.getOrderList(this.state.pageSize, ++this.state.pageNumber, false);
        }
        this.onEndReachedCalled = true;
    
      };
      _onRefresh() {
        this.setState({ nomore: false, pageNumber: 1 }, () => {
          this.getOrderList(this.state.pageSize, 1, true);
        })
    
      }
      renderItem(item){
        return (
          <View style={styles.item}>
            <Text>{item.name}</Text>
          </View>
        );
      };
    
      _keyExtractor = (item) => item.id;
    
    
      render() {
        return (
          <SafeAreaView style={BaseStyle.flex}>
            <View style={styles.listConten}>
            <Pull_Load
              data={this.state.data}
              keyExtractor={this._keyExtractor}
              onEndReached={this._onEndReached}
              nomore={this.state.nomore}
              renderItem={this.renderItem.bind(this)}
              onRefresh={this._onRefresh.bind(this)}
            />
            </View>
          </SafeAreaView>
        );
      }
    }
    
    const styles = StyleSheet.create({
      listConten: {
        flex: 1,
        backgroundColor: '#ffffff',
      },
      item: {
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: "center",
        backgroundColor: '#ffffff',
        height: 50,
      },
    
    
      
      
    });
    
    

    代码都很简单易懂~ 有什么不懂的,或者有什么问题请留言

    相关文章

      网友评论

        本文标题:react native基于FlatList下拉刷新上拉加载实现

        本文链接:https://www.haomeiwen.com/subject/vjiboftx.html