react-native系列(9)组件篇:最优列表显示方案FlatList和SectionList

FlatList列表

FlatList是一个高性能的列表组件。原理是:只负责渲染当前可见的列表项,对于不可见的项将不会渲染因为可见的项总是有限的,当一个项被划出屏幕后,被滑出项的容器将会成为新滑入的项的容器而不会重新再渲染一个,因此性能要比ScrollView和ListView组件高。

下面是一个原理简图:

FlatList的属性和方法:

属性描述

style

显示样式

data

数据源,格式为对象数组,如[{key:1},{key:2}]

renderItem

列表项渲染函数,数据来源于数据源遍历出的每个对象

showsVerticalScrollIndicator

当此属性为true的时候,显示一个垂直方向的滚动条,默认为: true

showsHorizontalScrollIndicator

当此属性为true的时候,显示一个水平方向的滚动条,默认为: true

pagingEnabled

当值为true时,滚动条会停在滚动视图的尺寸的整数倍位置。默认为: false

ItemSeparatorComponent

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

ListEmptyComponent

列表为空时渲染该组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element

ListFooterComponent

列表尾部组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element

ListHeaderComponent

列表头部组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element

numColumns

多列布局只能在非水平模式下使用,即必须是horizontal={false}。此时组件内元素会从左到右从上到下按 Z 字形排列。如numColumns = {2}表示列表显示两列

columnWrapperStyle

当numColumns大于1时才可用,表示指定此样式作用在每行容器上。如{{backgroundColor:'red'}}

horizontal

设置为 true 则变为水平布局模式,默认为: false

initialNumToRender

指定一开始渲染的元素数量,最好刚刚够填满一个屏幕,这样保证了用最短的时间给用户呈现可见的内容。注意这第一批次渲染的元素不会在滑动过程中被卸载,这样是为了保证用户执行返回顶部的操作时,不需要重新渲染首批元素

inverted

翻转滚动方向

onEndReachedThreshold

决定当距离内容最底部还有多远时触发onEndReached回调,范围0~1,如0.01表示触底时触发

onEndReached

在列表底部往下滑时触发该函数。表示当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用

scrollEnabled

当为false时表示禁止滚动,默认为: true

onMomentumScrollBegin

滚动惯性动画开始时触发的函数

onMomentumScrollEnd

滚动惯性动画结束时触发的函数

onScrollBeginDrag

拖拽开始时触发的函数

onScrollEndDrag

拖拽结束时触发的函数

onRefresh

在列表顶部往下滑时触发该函数。如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能,此时显示的loading符号为默认样式,需要自定义样式可使用refreshControl

refreshing

在等待加载新数据时将此属性设为 true,列表就会显示出一个正在加载的符号,此时显示的loading符号为默认样式,需要自定义样式可使用refreshControl

refreshControl

RefreshControl组件,可以自定义loading符号的样式,(该属性在中文官网中没有找到,应该是作者忽略了)

方法描述

scrollToEnd()

滚动到最底部

scrollToIndex()

将位于索引值为index的元素滚动到可视区域首行位置,如scrollToIndex({animated: true, index:10});

flashScrollIndicators()

短暂地显示滚动指示器

贴上代码:

import React, { Component } from 'react';
import { View, Text, FlatList, StyleSheet,RefreshControl } from 'react-native';

class FlatListComp extends Component {

    state = {
        list: [],
        refreshing: false
    };

    componentDidMount(){
        // 初始化数据
        let list = [];
        for(var i = 0; i < 8; i++) {
            list.push({key: 'key'+(i+1)});
        }
        this.setState({list: list});
    }

    // 渲染列表项
    _renderItem = ({index, item}) => {
        console.log(index);
        return (
            <View key={item.key} style={styles.itemViewStyle}>
                <Text style={styles.itemTextStyle}>{item.key}</Text>
            </View>
        );
    }

    // 分割线
    _renderSeparator = () => {
        return (
            class Separator extends Component {
                render(){
                    return (
                        <View style={styles.separatorStyle} />
                    );
                }
            }
        );
    }

    _renderListEmptyComp = () => {
        return (
            <View>
                 <Text>没有数据时显示本段文字</Text>
            </View>
        );
    }

    // 底部加载
    _onEndReached = () => {
        this.setState({refreshing: true});
        // 关于更新state里数组的两种方式
        //setState({ 'arrary': [...this.state.array, newItem]}).
        //setState({ 'array' : [...this.state.array].concat(newList|newItem)}).
        let newList = [];
        for(var i = 0; i < 3; i++) {
            newList.push({key: '(new)key'+ Math.floor(Math.random() * 10000)});
        }

        setTimeout(()=>{
            this.setState({list: [...this.state.list].concat(newList), refreshing: false});
        },2000);
    }

    // 顶部加载
    _onRefresh = () => {
        this.setState({refreshing: true});
        setTimeout(()=>{
            this.setState({refreshing: false});
            // this.myFlatList.scrollToEnd(); // 滚动到底部
            // this.myFlatList.scrollToIndex({animated: true, index:10}); // 将位于索引值为index的元素滚动到可视区域首行位置
            // this.myFlatList.flashScrollIndicators(); // 短暂地显示滚动指示器
        },2000);
    }

    render() {
        console.log(this.state.list);
        return (
            <View style={{flex:1}}>
                <View style={styles.headerViewStyle}>
                    <Text style={styles.headerTextStyle}>我的APP</Text>
                </View>
                <FlatList
                    style={styles.scrollViewStyle}
                    ref={(view) => { this.myFlatList = view; }}
                    data={this.state.list} // 数据源
                    renderItem={this._renderItem} // 从数据源中挨个取出数据并渲染到列表中
                    showsVerticalScrollIndicator={false} // 当此属性为true的时候,显示一个垂直方向的滚动条,默认为: true
                    showsHorizontalScrollIndicator={false} // 当此属性为true的时候,显示一个水平方向的滚动条,默认为: true
                    ItemSeparatorComponent = {this._renderSeparator()} // 行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后
                    ListEmptyComponent = {this._renderListEmptyComp()} // 列表为空时渲染该组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element
                    onEndReachedThreshold={0.01} // 决定当距离内容最底部还有多远时触发onEndReached回调,范围0~1,如0.01表示触底时触发
                    onEndReached={this._onEndReached} // 在列表底部往下滑时触发该函数。表示当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用
                    refreshControl={
                        <RefreshControl
                            refreshing={this.state.refreshing} // 在等待加载新数据时将此属性设为 true,列表就会显示出一个正在加载的符号
                            onRefresh={this._onRefresh.bind(this)} // 在列表顶部往下滑时触发该函数。如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能
                            tintColor="#ffffff" // 指定刷新指示器的背景色(iOS)
                            title="加载中..." // 指定刷新指示器下显示的文字(iOS)
                            titleColor="#000000" // 指定刷新指示器下显示的文字的颜色(iOS)
                            colors={['#ff0000', '#00ff00', '#0000ff']} // 刷新指示器在刷新期间的过渡颜色(Android)
                            progressBackgroundColor="#ffffff" // 指定刷新指示器的背景色(Android)
                        />
                    }
                />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    scrollViewStyle: {
        flex: 1, 
        marginLeft:10, 
        marginRight: 10, 
        marginBottom: 10
    },
    headerViewStyle: {
        height: 50,
        backgroundColor: '#f4511e',
        justifyContent: 'center',
        alignItems: 'center'
    },
    headerTextStyle: {
        fontSize: 20,
        color: '#FFFFFF'
    },
    itemViewStyle: {
        height: 100,
        borderWidth: 1,
        borderRadius: 10,
        marginTop: 5,
        justifyContent: 'center',
        alignItems: 'center'
    },
    itemTextStyle: {
        color: 'red',
        fontSize: 20
    },
    separatorStyle: {
        borderColor: '#A4A4A4',
        borderBottomWidth: 2,
        marginTop: 5
    }
});

export default FlatListComp;

效果:

小贴士:由于加载的loading符号形状是不可变的,如果你想要更个性化的列表组件,可以尝试使用第三方插件 react-native-pull 。

 

SectionList列表组

SectionList列表组和FlatList的原理是差不多的,在显示上,每个组里的项就是一个列表,要注意的是数据源格式的不同。

数据源的格式如下所示:

datasource=[
    {
        title:'section1',
        ...
        data:[
            {key:1},
            {key:2}
        ]
    }, ...]

在属性和方法上,大部分也和FlatList一样的,相同的这里就不列出来了。SectionList特有的属性和方法:

属性描述

sections

列表组数据源

renderSectionHeader

每个组的头部组件

renderSectionFooter

每个组的尾部组件

stickySectionHeadersEnabled

当下一个section把它的前一个section的可视区推离屏幕的时候,让这个section的header粘连在屏幕的顶端。默认为: false

SectionSeparatorComponent

组与组之间的分割线组件

方法描述

scrollToLocation()

将位于索引值为sectionIndex中itemIndex的元素滚动到可视区域首行位置,如scrollToLocation({animated: true, itemIndex:2, sectionIndex:1});

贴上代码(本demo中代码有点多,因为列出了所有的列表组属性,请仔细看):

import React, {Component} from 'react';
import { View, Text, SectionList, StyleSheet, RefreshControl } from 'react-native';

class SectionListComp extends Component {

    state = {
        sections: [],
        refreshing: false
    };
    
    componentDidMount() {
        let sections = [];
        for(var i = 0; i < 4; i++) {
            let section = {};
            section.title = '列表组'+ (i+1);
            section.data = [];
            for(var n = 0; n < 5; n++) {
                let item = {};
                item.key = 'key' + (n+1);
                section.data.push(item);
            }
            sections.push(section);
        }
        this.setState({sections: sections});
    }

    _renderSectionHeader = ({section: {title}}) => {
        return (
            <View style={styles.titleViewStyle}>
                <Text style={styles.titleTextStyle}>{title}</Text>
            </View>
        );
    }

    _renderItem = ({ item, index, section }) => {
        return (
            <View style={styles.itemViewStyle}>
                <Text style={styles.itemTextStyle} key={item.key}>{item.key}</Text>
            </View>
        );
    }

    _renderListEmptyComp = () => {
        return (
            <View>
                 <Text>没有数据时显示本段文字</Text>
            </View>
        );
    }

    // 分割线
    _renderSeparator = () => {
        return (
            class Separator extends Component {
                render(){
                    return (
                        <View style={styles.separatorStyle} />
                    );
                }
            }
        );
    }

    // 底部加载
    _onEndReached = () => {
        this.setState({refreshing: true});
        // 关于更新state里数组的两种方式
        //setState({ 'arrary': [...this.state.array, newItem]}).
        //setState({ 'array' : [...this.state.array].concat(newList|newItem)}).
        let sections = [...this.state.sections];
        let section = {};
        section.title = '(new)列表组'+ Math.floor(Math.random() * 10000);
        section.data = [];
        for(var n = 0; n < 5; n++) {
            let item = {};
            item.key = 'key' + n;
            section.data.push(item);
        }
        sections.push(section);

        setTimeout(()=>{
            this.setState({sections: sections, refreshing: false});
        },2000);
    }

    // 顶部加载
    _onRefresh = () => {
        this.setState({refreshing: true});
        setTimeout(()=>{
            this.setState({refreshing: false});
            // this.mySectionList.scrollToLocation({animated: true, itemIndex:2, sectionIndex:1}); // 将位于索引值为sectionIndex中itemIndex的元素滚动到可视区域首行位置
            // this.myFlatList.flashScrollIndicators(); // 短暂地显示滚动指示器
        },2000);
    }

    render() {
        return (
            <View style={{flex:1}}>
                <View style={styles.headerViewStyle}>
                    <Text style={styles.headerTextStyle}>我的APP</Text>
                </View>
                <SectionList
                    ref={(view) => { this.mySectionList = view; }}
                    style={styles.scrollViewStyle}
                    sections={this.state.sections}
                    renderItem={this._renderItem}
                    renderSectionHeader={this._renderSectionHeader} // 每个组的头部组件
                    renderSectionFooter={()=>{}} // 每个组的尾部组件
                    ListHeaderComponent = {()=><View />} // 头部组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element
                    ListFooterComponent = {()=><View />} // 尾部组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element
                    ListEmptyComponent = {this._renderListEmptyComp()} // 列表为空时渲染该组件。可以是 React Component, 也可以是一个 render 函数,或者渲染好的 element
                    // ItemSeparatorComponent = {this._renderSeparator()} // 行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后
                    // SectionSeparatorComponent = {this._renderSeparator()} // 组与组之间的分割线组件
                    inverted={false} // 翻转滚动方向。默认为: false
                    stickySectionHeadersEnabled = {true} // 当下一个section把它的前一个section的可视区推离屏幕的时候,让这个section的header粘连在屏幕的顶端。默认为: false
                    horizontal= {false} // 设置为 true 则变为水平布局模式,默认为: false
                    showsVerticalScrollIndicator={false} // 当此属性为true的时候,显示一个垂直方向的滚动条,默认为: true
                    showsHorizontalScrollIndicator={false} // 当此属性为true的时候,显示一个水平方向的滚动条,默认为: true
                    scrollEnabled={true} // 当为false时表示禁止滚动,默认为: true
                    onEndReachedThreshold={0.01} // 决定当距离内容最底部还有多远时触发onEndReached回调,范围0~1,如0.01表示触底时触发
                    onEndReached={this._onEndReached} // 表示当列表被滚动到距离内容最底部不足onEndReachedThreshold的距离时调用
                    onMomentumScrollBegin={()=>{}} // 滚动惯性动画开始时触发的函数
                    onMomentumScrollEnd={()=>{}} // 滚动惯性动画结束时触发的函数
                    onScrollBeginDrag={()=>{}} // 拖拽开始时触发的函数
                    onScrollEndDrag={()=>{}} // 拖拽结束时触发的函数
                    // initialNumToRender={6} // 指定一开始渲染的元素数量,最好刚刚够填满一个屏幕,这样保证了用最短的时间给用户呈现可见的内容。注意这第一批次渲染的元素不会在滑动过程中被卸载,这样是为了保证用户执行返回顶部的操作时,不需要重新渲染首批元素
                    // keyExtractor={(item, index) => item + index} // 当item没有key属性时,可以通过该函数生成一个不重复的key值
                    // onRefresh={this._onRefresh} // 如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能
                    // refreshing={this.state.refreshing} // 在等待加载新数据时将此属性设为 true,列表就会显示出一个正在加载的符号
                    或
                    refreshControl={
                        <RefreshControl
                            refreshing={this.state.refreshing} // 在等待加载新数据时将此属性设为 true,列表就会显示出一个正在加载的符号
                            onRefresh={this._onRefresh.bind(this)} // 在列表顶部往下滑时触发该函数。如果设置了此选项,则会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能
                            tintColor="#ffffff" // 指定刷新指示器的背景色(iOS)
                            title="加载中..." // 指定刷新指示器下显示的文字(iOS)
                            titleColor="#000000" // 指定刷新指示器下显示的文字的颜色(iOS)
                            colors={['#ff0000', '#00ff00', '#0000ff']} // 刷新指示器在刷新期间的过渡颜色(Android)
                            progressBackgroundColor="#ffffff" // 指定刷新指示器的背景色(Android)
                        />
                    }
                />
            </View>
        );
    }
}

const styles = StyleSheet.create({
    scrollViewStyle: {
        flex: 1, 
        marginLeft: 10,
        marginRight: 10
    },
    headerViewStyle: {
        height: 50,
        backgroundColor: '#f4511e',
        justifyContent: 'center',
        alignItems: 'center'
    },
    headerTextStyle: {
        fontSize: 20,
        color: '#FFFFFF'
    },
    titleViewStyle: {
        backgroundColor: '#E4E4E4',
        marginTop: 20,
        height: 30,
        justifyContent: 'center',
        alignItems: 'center'
    },
    titleTextStyle: {
        color: 'red',
        fontSize: 24
    },
    itemViewStyle: {
        height: 70,
        borderWidth: 1,
        borderRadius: 10,
        marginTop: 5,
        justifyContent: 'center',
        alignItems: 'center'
    },
    itemTextStyle: {
        color: 'red',
        fontSize: 20
    },
    separatorStyle: {
        borderColor: 'blue',
        borderBottomWidth: 1,
        marginTop: 5,
        marginBottom: 5
    }
});

export default SectionListComp;

效果:

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值