• react-native 模仿原生 实现下拉刷新/上拉加载更多(RefreshListView)


    1.下拉刷新/上拉加载更多 组件(RefreshListView)

    src/components/RefreshListView/index.js

    /**
     * 下拉刷新/上拉加载更多 组件(RefreshListView)
     */
    import React, {PureComponent} from 'react';
    import PropTypes from 'prop-types';
    import {
      View,
      Text,
      StyleSheet,
      FlatList,
      ActivityIndicator,
      TouchableOpacity,
      ViewPropTypes,
      RefreshControl
    } from 'react-native'
    
    const RefreshState = {
      Idle: 0,
      HeaderRefreshing: 1,
      FooterRefreshing: 2,
      NoMoreData: 3,
      Failure: 4,
      EmptyData: 5,
    }
    
    class RefreshListView extends PureComponent {
      static propTypes = {
        data: PropTypes.array.isRequired,
        renderItem: PropTypes.func.isRequired,
        refreshState: PropTypes.number.isRequired,
    
        listRef: PropTypes.node,
        onHeaderRefresh: PropTypes.func,
        footerContainerStyle: ViewPropTypes.style,
        footerTextStyle: ViewPropTypes.style,
    
        disabledSeparator: PropTypes.bool,
        disabledHeaderRefresh: PropTypes.bool,
        footerRefreshingText: PropTypes.string,
        footerFailureText: PropTypes.string,
        footerNoMoreDataText: PropTypes.string,
        footerEmptyDataText: PropTypes.string,
    
        ListEmptyComponent: PropTypes.node,
        footerRefreshingComponent: PropTypes.node,
        footerFailureComponent: PropTypes.node,
        footerNoMoreDataComponent: PropTypes.node,
        footerEmptyDataComponent: PropTypes.node,
      }
    
      static defaultProps = {
        disabledHeaderRefresh: false,
        footerRefreshingText: '数据加载中…',
        footerFailureText: '点击重新加载',
        footerNoMoreDataText: '已加载全部数据',
        footerEmptyDataText: '暂时没有相关数据',
      }
    
      componentWillReceiveProps(nextProps) {}
    
      componentDidUpdate(prevProps, prevState) {}
    
      onHeaderRefresh = () => {
        if (this.shouldStartHeaderRefreshing()) {
          this.props.onHeaderRefresh(RefreshState.HeaderRefreshing)
        }
      }
    
      onEndReached = () => {
        if (this.shouldStartFooterRefreshing()) {
          this.props.onFooterRefresh && this.props.onFooterRefresh(RefreshState.FooterRefreshing)
        }
      }
    
      shouldStartHeaderRefreshing = () => {
        if (this.props.refreshState == RefreshState.HeaderRefreshing || this.props.refreshState == RefreshState.FooterRefreshing) {
          return false
        }
        return true
      }
    
      shouldStartFooterRefreshing = () => {
        const {refreshState, data} = this.props
        if (data.length == 0) {
          return false
        }
        return (refreshState == RefreshState.Idle)
      }
    
      renderSeparator = () => (
        <View style={{height: 1, backgroundColor: '#e0e0e0'}} />
      )
    
      renderFooter = () => {
        let footer = null
    
        let {
          footerRefreshingText,
          footerFailureText,
          footerNoMoreDataText,
          footerEmptyDataText,
    
          footerRefreshingComponent,
          footerFailureComponent,
          footerNoMoreDataComponent,
          footerEmptyDataComponent,
        } = this.props
    
        switch (this.props.refreshState) {
          case RefreshState.Idle: {
            footer = (<View style={styles.footerContainer} />)
            break
          }
          case RefreshState.Failure: {
            footer = (
              <TouchableOpacity onPress={() => {
                if (this.props.data.length == 0) {
                  this.props.onHeaderRefresh && this.props.onHeaderRefresh(RefreshState.HeaderRefreshing)
                } else {
                  this.props.onFooterRefresh && this.props.onFooterRefresh(RefreshState.FooterRefreshing)
                }
              }}
              >
                {footerFailureComponent ? footerFailureComponent : (
                  <View style={styles.footerContainer}>
                    <Text style={styles.footerText}>{footerFailureText}</Text>
                  </View>
                )}
              </TouchableOpacity>
            )
            break
          }
          case RefreshState.EmptyData: {
            footer = (
              <TouchableOpacity onPress={() => {
                this.props.onHeaderRefresh && this.props.onHeaderRefresh(RefreshState.HeaderRefreshing)
              }}
              >
                {footerEmptyDataComponent ? footerEmptyDataComponent : (
                  <View style={styles.footerContainer}>
                    <Text style={styles.footerText}>{footerEmptyDataText}</Text>
                  </View>
                )}
              </TouchableOpacity>
            )
            break
          }
          case RefreshState.FooterRefreshing: {
            footer = footerRefreshingComponent ? footerRefreshingComponent : (
              <View style={styles.footerContainer} >
                <ActivityIndicator size="small" color="#888888" />
                <Text style={[styles.footerText, {marginLeft: 7}]}>{footerRefreshingText}</Text>
              </View>
            )
            break
          }
          case RefreshState.NoMoreData: {
            footer = footerNoMoreDataComponent ? footerNoMoreDataComponent : (
              <View style={styles.footerContainer} >
                <Text style={styles.footerText}>{footerNoMoreDataText}</Text>
              </View>
            )
            break
          }
        }
        return footer
      }
    
      render() {
        const {renderItem, ...rest} = this.props
        return (
          <FlatList
            ref={this.props.listRef}
            {...rest}
            // 行与行之间的分隔线组件
            ItemSeparatorComponent={this.props.disabledSeparator?false:this.renderSeparator}
            // 列表为空时渲染该组件
            ListEmptyComponent={this.props.ListEmptyComponent}
            // 头部组件
            ListHeaderComponent={this.props.renderHeader}
            // 尾部组件
            ListFooterComponent={this.renderFooter}
            // 当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用
            onEndReached={this.onEndReached}
            // 刷新组件
            refreshControl={
              this.props.disabledHeaderRefresh?false:<RefreshControl
              colors={['#00ff00',"#9Bd35A", "#689F38",]}
              refreshing={this.props.refreshState == RefreshState.HeaderRefreshing}
              onRefresh={this.onHeaderRefresh}
            />}
            // 决定当距离内容最底部还有多远时触发onEndReached回调
            onEndReachedThreshold={0.1}
            // 根据行数据data,渲染每一行的组件
            renderItem={renderItem}
          />
        )
      }
    }
    
    const styles = StyleSheet.create({
      footerContainer: {
        flex: 1,
        flexDirection: 'row',
        justifyContent: 'center',
        alignItems: 'center',
        padding: 10,
        height: 44,
      },
      footerText: {
        fontSize: 14,
        color: '#555555',
      },
    })
    
    export {
      RefreshState,
    }
    
    export default RefreshListView;

    2.页面调用

    (1)定义全局变量

    // 刷新状态
    global.RefreshState = {
      Idle: 0, // 加载成功
      HeaderRefreshing: 1, // 开始下拉刷新
      FooterRefreshing: 2, // 开始上拉翻页
      NoMoreData: 3, // 加载全部数据
      Failure: 4, // 加载失败
      EmptyData: 5, // 服务器没有数据
    }
    

    (2)通用store

    @observable
    refreshState: any;
    
    /**
     * 改变refreshState的值
     * @param refreshState
     */
    @action setRefreshState(refreshState) {
      this.refreshState = refreshState
    }
    

    (3)当前 store

    // 加载成功
    this.setRefreshState(RefreshState.Idle);
    
    if(!res.data.topics.length){
      // 服务器没有数据
      this.setRefreshState(RefreshState.EmptyData);
    }
    

    (4)页面

    const { data, refreshState, loadData, loadMoreData } = this.store;
    
    // 新闻列表
    store = new NewsStore();
     
    // 子组件渲染
    _renderRow(obj) {
      let item = obj.item;
      return (
        <ListRow
          key={item.id}
          title={item.title}
          onPress={() => {
            // 跳转详情页
            Actions.homeDetailPage({detail: item})
          }}
        />
      )
    }
    
    <RefreshListView
      data={toJS(data)}
      keyExtractor={(item,index) => index.toString()}
      renderItem={this._renderRow.bind(this)}
    
      refreshState={refreshState}
      onHeaderRefresh={loadData.bind(this.store)}
      onFooterRefresh={loadMoreData.bind(this.store)}
    />

    3.效果图

  • 相关阅读:
    【Luogu】P3369 【模板】普通平衡树(树状数组)
    文艺平衡树 lg3391(splay维护区间入门)
    普通平衡树 lg3369
    noip2018游记
    webview与壳交互的几种方式
    iOS、Android 之类的如何调试 Web APP
    box-sizing属性
    Hybridapp /webapp调试工具
    DOS 批处理高级教程精选合编
    瀑布流Masonry学习
  • 原文地址:https://www.cnblogs.com/crazycode2/p/9533261.html
Copyright © 2020-2023  润新知