美文网首页React Native学习RNReact native
React Native FlatList上拉加载

React Native FlatList上拉加载

作者: 街角仰望 | 来源:发表于2018-06-05 13:55 被阅读508次

    我们可以利用官方组件 RefreshControl实现下拉刷新功能,但React Native官方没有提供相应的上拉加载的组件,因此在RN中实现上拉加载比下拉刷新要复杂一点。
    虽然没有直接提供上拉加载的组件,不过我们仍可以通过FlatListonEndReachedonEndReachedThreshold属性来实现相应效果。

    ActivityIndicator

    这里上拉加载的转圈效果用ActivityIndicator表现。在开始上拉加载的实现之前先介绍一下官方组件ActivityIndicator——加载指示器ActivityIndicator的使用很简单。

    <ActivityIndicator
            animating={true}
            color='red'
            size="large"
        />
    

    属性

    属性 类型 描述
    animating bool 是否要显示指示器,默认为true,表示显示。
    color string 滚轮的前景颜色(默认为灰色)。
    hidesWhenStopped(ios) bool 在没有动画的时候,是否要隐藏指示器(默认为true)。
    size enum(‘small’, ‘large’) 指示器的大小。small的高度为20,large为36。

    加载页

    根据上述ActivityIndicator属性的介绍,可以使用ActivityIndicator实现一个简单的加载页面:

    import React, {Component} from "react";
    import {ActivityIndicator,  StyleSheet,  View} from "react-native";
    
    export default class ActivityIndicatorDemo extends Component {
        render() {
            return (
                <View style={styles.container}>
                    <ActivityIndicator
                        animating={true}
                        color='red'
                        size="large"
                    />
                </View>
            );
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            flexDirection: 'row',
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#F5FCFF',
        },
    });
    
    其效果如下:

    上拉加载的实现

    下面进入正篇——上拉加载的实现
    上拉加载一般应用于分页加载的情况,为了真实的模拟网络请求数据上拉刷新加载更多的使用场景,这里使用github提供的api

    思路

    FlatList滑动到底部时,页面数加一,触发请求新一页的网络数据,更新到组件state中的数据源dataArray中,dataArray也作为FlatList的数据源data。实现滑动到底部触发网络请求通过FlatListonEndReachedonEndReachedThreshold属性,onEndReached是在当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用。

    具体实现

    全局变量
    const REQUEST_URL = 'https://api.github.com/search/repositories?q=javascript&sort=stars&page=';
    let pageNo = 1;//当前第几页
    let totalPage=5;//总的页数
    let itemNo=0;//item的个数
    
    初始化state
    constructor(props) {
            super(props);
            this.state = {
                isLoading: true,
                //网络请求状态
                error: false,
                errorInfo: "",
                dataArray: [],
                showFoot:0, // 控制foot, 0:隐藏footer  1:已加载完成,没有更多数据   2 :显示加载中
                isRefreshing:false,//下拉控制
            }
        }
    
    网络请求——获取第n页数据
    //网络请求——获取第pageNo页数据
        fetchData(pageNo) {
            //这个是js的访问网络的方法
            fetch(REQUEST_URL+pageNo)
                .then((response) => response.json())
                .then((responseData) => {
                    let data = responseData.items;
                    let dataBlob = [];
                    let i = itemNo;
    
                    data.map(function (item) {
                        dataBlob.push({
                            key: I,
                            value: item,
                        })
                        I++;
                    });
                    itemNo = I;
                    console.log("itemNo:"+itemNo);
                    let foot = 0;
                    if(pageNo>=totalPage){
                        foot = 1;//listView底部显示没有更多数据了
                    }
    
                    this.setState({
                        //复制数据源
                        dataArray:this.state.dataArray.concat(dataBlob),
                        isLoading: false,
                        showFoot:foot,
                        isRefreshing:false,
                    });
                    data = null;
                    dataBlob = null;
                })
                .catch((error) => {
                    this.setState({
                        error: true,
                        errorInfo: error
                    })
                })
                .done();
        }
    
    准备加载组件

    在初始化state之后,渲染组件前,请求网络数据。

    componentDidMount() {
            //请求数据
            this.fetchData( pageNo );
        }
    
    渲染组件
    render() {
            //第一次加载等待的view
            if (this.state.isLoading && !this.state.error) {
                return this.renderLoadingView();
            } else if (this.state.error) {
                //请求失败view
                return this.renderErrorView();
            }
            //加载数据
            return this.renderData();
        }
    
    加载等待页

    利用上面介绍的ActivityIndicator,实现加载等待页。

    renderLoadingView() {
            return (
                <View style={styles.container}>
                    <ActivityIndicator
                        animating={true}
                        color='red'
                        size="large"
                    />
                </View>
            );
        }
    
    加载网络请求失败页

    网络请求失败,显示失败信息页。

    renderErrorView() {
            return (
                <View style={styles.container}>
                    <Text>
                        Fail
                    </Text>
                </View>
            );
        }
    
    加载数据显示页
    renderData() {
            return (
                <FlatList
                    data={this.state.dataArray}
                    renderItem={this._renderItemView}
                    ListFooterComponent={this._renderFooter.bind(this)}
                    onEndReached={this._onEndReached.bind(this)}
                    onEndReachedThreshold={1}
                    ItemSeparatorComponent={this._separator}
                />
    
            );
        }
    
    每个item组件渲染

    renderItem根据行数据data渲染每一行的组件。

    //返回itemView
        _renderItemView({item}) {
            return (
                <View>
                    <Text style={styles.title}>name: {item.value.name} ({item.value.stargazers_count}
                        stars)</Text>
                    <Text style={styles.content}>description: {item.value.description}</Text>
                </View>
            );
        }
    
    尾部组件的渲染

    ListFooterComponent为尾部组件的渲染
    showFoot为2时显示loading的动态效果是用ActivityIndicator来实现的。

     _renderFooter(){
            if (this.state.showFoot === 1) {
                return (
                    <View style={{height:30,alignItems:'center',justifyContent:'flex-start',}}>
                        <Text style={{color:'#999999',fontSize:14,marginTop:5,marginBottom:5,}}>
                            没有更多数据了
                        </Text>
                    </View>
                );
            } else if(this.state.showFoot === 2) {
                return (
                    <View style={styles.footer}>
                        <ActivityIndicator />
                        <Text>正在加载更多数据...</Text>
                    </View>
                );
            } else if(this.state.showFoot === 0){
                return (
                    <View style={styles.footer}>
                        <Text></Text>
                    </View>
                );
            }
        }
    
    上拉加载

    上拉加载的关键onEndReached,当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用。注意:onEndReachedThreshold的值不是像素单位而是比值,例如,0.5表示距离内容最底部的距离为当前列表可见长度的一半时触发。

    _onEndReached(){
            //如果是正在加载中或没有更多数据了,则返回
            if(this.state.showFoot != 0 ){
                return ;
            }
            //如果当前页大于或等于总页数,那就是到最后一页了,返回
            if((pageNo!=1) && (pageNo>=totalPage)){
                return;
            } else {
                pageNo++;
            }
            //底部显示正在加载更多数据
            this.setState({showFoot:2});
            //获取数据
            this.fetchData( pageNo );
        }
    }
    
    分隔线

    ItemSeparatorComponent行与行之间的分隔线组件,不会出现在第一行之前和最后一行之后。

    _separator(){
            return <View style={{height:1,backgroundColor:'#999999'}}/>;
        }
    
    效果图如下:
    最后附上完整代码:
    import React, {Component} from "react";
    import {ActivityIndicator, FlatList, StyleSheet, Text, View} from "react-native";
    
    const REQUEST_URL = 'https://api.github.com/search/repositories?q=javascript&sort=stars&page=';
    let pageNo = 1;//当前第几页
    let totalPage=5;//总的页数
    let itemNo=0;//item的个数
    export default class LoadMoreDemo extends Component {
        constructor(props) {
            super(props);
            this.state = {
                isLoading: true,
                //网络请求状态
                error: false,
                errorInfo: "",
                dataArray: [],
                showFoot:0, // 控制foot, 0:隐藏footer  1:已加载完成,没有更多数据   2 :显示加载中
                isRefreshing:false,//下拉控制
            }
        }
    
        //网络请求——获取第pageNo页数据
        fetchData(pageNo) {
            //这个是js的访问网络的方法
            fetch(REQUEST_URL+pageNo)
                .then((response) => response.json())
                .then((responseData) => {
                    let data = responseData.items;
                    let dataBlob = [];
                    let i = itemNo;
    
                    data.map(function (item) {
                        dataBlob.push({
                            key: i,
                            value: item,
                        })
                        i++;
                    });
                    itemNo = i;
                    console.log("itemNo:"+itemNo);
                    let foot = 0;
                    if(pageNo>=totalPage){
                        foot = 1;//listView底部显示没有更多数据了
                    }
    
                    this.setState({
                        //复制数据源
                        dataArray:this.state.dataArray.concat(dataBlob),
                        isLoading: false,
                        showFoot:foot,
                        isRefreshing:false,
                    });
                    data = null;
                    dataBlob = null;
                })
                .catch((error) => {
                    this.setState({
                        error: true,
                        errorInfo: error
                    })
                })
                .done();
        }
    
        componentDidMount() {
            //请求数据
            this.fetchData( pageNo );
        }
    
        //加载等待页
        renderLoadingView() {
            return (
                <View style={styles.container}>
                    <ActivityIndicator
                        animating={true}
                        color='red'
                        size="large"
                    />
                </View>
            );
        }
    
        //加载失败view
        renderErrorView() {
            return (
                <View style={styles.container}>
                    <Text>
                        Fail
                    </Text>
                </View>
            );
        }
    
        //返回itemView
        _renderItemView({item}) {
            return (
                <View>
                    <Text style={styles.title}>name: {item.value.name}</Text>
                    <Text style={styles.content}>stars: {item.value.stargazers_count}</Text>
                    <Text style={styles.content}>description: {item.value.description}</Text>
                </View>
            );
        }
    
        renderData() {
            return (
    
                <FlatList
                    data={this.state.dataArray}
                    renderItem={this._renderItemView}
                    ListFooterComponent={this._renderFooter.bind(this)}
                    onEndReached={this._onEndReached.bind(this)}
                    onEndReachedThreshold={1}
                    ItemSeparatorComponent={this._separator}
                />
    
            );
        }
    
        render() {
            //第一次加载等待的view
            if (this.state.isLoading && !this.state.error) {
                return this.renderLoadingView();
            } else if (this.state.error) {
                //请求失败view
                return this.renderErrorView();
            }
            //加载数据
            return this.renderData();
        }
        _separator(){
            return <View style={{height:1,backgroundColor:'#999999'}}/>;
        }
        _renderFooter(){
            if (this.state.showFoot === 1) {
                return (
                    <View style={{height:30,alignItems:'center',justifyContent:'flex-start',}}>
                        <Text style={{color:'#999999',fontSize:14,marginTop:5,marginBottom:5,}}>
                            没有更多数据了
                        </Text>
                    </View>
                );
            } else if(this.state.showFoot === 2) {
                return (
                    <View style={styles.footer}>
                        <ActivityIndicator />
                        <Text>正在加载更多数据...</Text>
                    </View>
                );
            } else if(this.state.showFoot === 0){
                return (
                    <View style={styles.footer}>
                        <Text></Text>
                    </View>
                );
            }
        }
    
        _onEndReached(){
            //如果是正在加载中或没有更多数据了,则返回
            if(this.state.showFoot != 0 ){
                return ;
            }
            //如果当前页大于或等于总页数,那就是到最后一页了,返回
            if((pageNo!=1) && (pageNo>=totalPage)){
                return;
            } else {
                pageNo++;
            }
            //底部显示正在加载更多数据
            this.setState({showFoot:2});
            //获取数据
            this.fetchData( pageNo );
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            flexDirection: 'row',
            justifyContent: 'center',
            alignItems: 'center',
            backgroundColor: '#F5FCFF',
        },
        title: {
            fontSize: 15,
            color: 'blue',
        },
        footer:{
            flexDirection:'row',
            height:24,
            justifyContent:'center',
            alignItems:'center',
            marginBottom:10,
        },
        content: {
            fontSize: 15,
            color: 'black',
        }
    });
    

    参考:
    https://blog.csdn.net/teagreen_red/article/details/78340889

    相关文章

      网友评论

      本文标题:React Native FlatList上拉加载

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