zoukankan      html  css  js  c++  java
  • react-native 自定义 下拉刷新 / 上拉加载更多 组件

    1.封装 Scroller 组件

    src/components/Scroller/index.js

    /**
     * 下拉刷新/上拉加载更多 组件(Scroller)
     */
    import React, {Component} from 'react';
    import {
      StyleSheet,
      Text,
      View,
      ListView,
      ActivityIndicator,
      RefreshControl,
    } from 'react-native';
    
    export default class Scroller extends Component {
      // 构造函数
      constructor(props) {
        super(props);
        this.state = {
          //
        }
      }
    
      render() {
        const { dataSource, renderRow, isRefreshing } = this.props;
        // console.log(this.props);
    
        return (
          <View style={styles.container}>
            {/*列表数据*/}
            <ListView
              // 数据源
              dataSource={dataSource}
              // 从数据源(dataSource)中接受一条数据,以及它和它所在section的ID
              renderRow={renderRow}
              // 页头与页脚会在每次渲染过程中都重新渲染(允许在ListView底部增加一栏,便于显示加载动画)
              renderFooter={this._renderFooter.bind(this)}
              // 当所有的数据都已经渲染过,并且列表被滚动到距离最底部不足onEndReachedThreshold个像素的距离时调用
              onEndReached={this._fetchMoreData.bind(this)}
              // 调用onEndReached之前的临界值,单位是像素。(预加载)
              onEndReachedThreshold={20}
              // 隐藏右侧滚动条
              showsVerticalScrollIndicator={false}
              // finished warning : in next release ...
              enableEmptySections={true}
              // 自动调整迁移内容
              // 导航栏或标签栏或工具栏不覆盖 Scrollview 内容
              // 去除默认定位间距
              automaticallyAdjustContentInsets={false}
              // 下拉刷新
              refreshControl={
                <RefreshControl
                  // 是否刷新
                  refreshing={isRefreshing}
                  onRefresh={this._onRefresh.bind(this)}
                  tintColor={"#ff6600"}
                  title={"拼命加载中..."}
                />
              }
            />
          </View>
        )
      }
    
      /**
       * 下拉刷新
       */
      _onRefresh() {
        // console.log('下拉刷新');
        if (this.props.isRefreshing || !this._hasMore()) {
          return
        }
        // 向后台发送 '0',告知刷新操作
        this.props.fetchData(0);
      }
    
      /**
       * 加 _ 代表私有方法
       * 上拉加载更多
       */
      _fetchMoreData() {
        // console.log('上拉加载更多');
        /**
         * this._hasMore() 验证还有更多数据
         * isLoadingTail true/false 加载动画(菊花图)
         */
        if (!this._hasMore() || this.props.isLoadingTail) {
          return
        }
        let page = this.props.cachedResults.nextPage;
        this.props.fetchData(page);
      }
    
      /**
       * 验证还有更多数据
       */
      _hasMore() {
        return this.props.cachedResults.items.length !== this.props.cachedResults.items.total;
      }
    
      /**
       * 底部加载动画 及 没有更多数据文本(ListView底部增加一栏,便于显示加载动画)
       */
      _renderFooter() {
        if (!this._hasMore() && this.props.cachedResults.total !== 0) {
          return (
            <View style={styles.loadingMore}>
              <Text style={styles.loadingText}>没有更多了</Text>
            </View>
          )
        }
    
        if (!this.props.isLoadingTail) {
          return (
            <View style={styles.loadingMore}></View>
          )
        }
    
        // 菊花图
        return (
          <ActivityIndicator style={styles.loadingMore}/>
        )
      }
    
    }
    
    // 样式
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
      },
      // 菊花图
      loadingMore: {
        marginVertical: 20
      },
      // 文案样式
      loadingText: {
        color: '#777',
        textAlign: 'center'
      }
    });
    

    2.页面调用

    /**
     * 视频列表页
     */
    import React, {Component} from 'react';
    import {
      StyleSheet,
      Text,
      View,
      ImageBackground,
      ListView,
      TouchableHighlight,
      Alert,
      Dimensions,
      ActivityIndicator,
      RefreshControl,
    } from 'react-native';
    // 下拉刷新/上拉加载更多组件
    import Scroller from '../../components/Scroller';
    // 图标
    import Icon from 'react-native-vector-icons/Ionicons';
    // item 组件
    import CreationItem from '../../components/CreationItem';
    import config from '../../common/config';
    import request from '../../common/request';
    
    let {width} = Dimensions.get("window");
    
    // 缓存列表中所有数据
    let cachedResults = {
      nextPage: 1, // 下一页
      items: [], // listview 数据(视频列表)
      total: 0 // 总数
    };
    
    export default class List extends Component {
      // 构造函数
      constructor() {
        super();
        let ds = new ListView.DataSource({
          // 比较两条数据是否是一样的,来判断数据是否发生改变
          rowHasChanged: (r1, r2) => r1 !== r2
        });
        this.state = {
          dataSource: ds.cloneWithRows([]),
          isLoadingTail: false, // loading?
          isRefreshing: false // refresh?
        }
      }
    
      render() {
        return (
          <View style={styles.container}>
            {/*顶部标题栏*/}
            <View style={styles.header}>
              <Text style={styles.headerTitle}>列表页面</Text>
            </View>
            {/*列表数据*/}
            <Scroller
              // 数据源
              dataSource={this.state.dataSource}
              // 渲染item(子组件)
              renderRow={this._renderRow.bind(this)}
              // 是否可以刷新
              isRefreshing={this.state.isRefreshing}
              // 是否可以加载更多
              isLoadingTail={this.state.isLoadingTail}
              // 请求数据
              fetchData={this._fetchData.bind(this)}
              // 缓存列表数据
              cachedResults={cachedResults}
            />
          </View>
        )
      }
    
      // 生命周期-组件挂载完毕 请求数据
      componentDidMount() {
        this._fetchData(1);
      }
    
      // 请求数据
      _fetchData(page) {
        let that = this;
    
        if (page !== 0) { // 加载更多操作
          this.setState({
            isLoadingTail: true
          });
        } else { // 刷新操作
          this.setState({
            isRefreshing: true
          });
          // 初始哈 nextPage
          cachedResults.nextPage = 1;
        }
    
        request
          .get(config.api.base + config.api.creations, {
            accessToken: 'abc'
          })
          // data 变化的新数据
          .then((data) => {
            if (data.success) {
              // 保存原数据
              let items = cachedResults.items.slice();
              if (page !== 0) { // 加载更多操作
                // 数组拼接
                items = items.concat(data.data);
                cachedResults.nextPage += 1;
              } else { // 刷新操作
                // 数据不变
                items = data.data;
              }
    
              cachedResults.items = items; // 视频列表数据
              cachedResults.total = data.total; // 总数
    
              setTimeout(function () {
                if (page !== 0) { // 加载更多操作
                  that.setState({
                    isLoadingTail: false,
                    dataSource: that.state.dataSource.cloneWithRows(cachedResults.items)
                  });
                } else { // 刷次操作
                  that.setState({
                    isRefreshing: false,
                    dataSource: that.state.dataSource.cloneWithRows(cachedResults.items)
                  });
                }
              }, 1000);
            }
          })
          .catch((error) => {
            if (page !== 0) { // 上拉加载更多操作
              this.setState({
                isLoadingTail: false
              });
            } else {
              this.setState({ // 刷新操作
                isRefreshing: false
              });
            }
            console.error(error);
          });
      }
    
      // 列表 Item
      _renderRow(row) {
        const { navigation } = this.props;
        return (
          <CreationItem
            navigation={navigation}
            key={row.id} // 子组件唯一性
            row={row}
          />
        )
      }
    }
    
    // 样式
    const styles = StyleSheet.create({
      container: {
        flex: 1,
        backgroundColor: '#F5FCFF',
      },
      // 头部样式
      header: {
        paddingTop: 25,
        paddingBottom: 12,
        backgroundColor: '#ee735c',
      },
      // 头部title样式
      headerTitle: {
        color: '#fff',
        fontSize: 16,
        textAlign: 'center',
        fontWeight: '600'
      },
      // 菊花图
      loadingMore: {
        marginVertical: 20
      },
      // 文案样式
      loadingText: {
        color: '#777',
        textAlign: 'center'
      }
    });
    

    3.效果图

  • 相关阅读:
    8.10
    今日头条笔试题 1~n的每个数,按字典序排完序后,第m个数是什么?
    Gym 100500B Conference Room(最小表示法,哈希)
    CodeForces 438D The Child and Sequence(线段树)
    UVALIVE 6905 Two Yachts(最小费用最大流)
    Gym Conference Room (最小表示法,哈希)
    hdu 2389 Rain on your Parade(二分图HK算法)
    Codeforces Fox And Dinner(最大流)
    zoj 3367 Counterfeit Money(dp)
    ZOJ3370. Radio Waves(2-sat)
  • 原文地址:https://www.cnblogs.com/crazycode2/p/9375527.html
Copyright © 2011-2022 走看看