美文网首页React Native学习RN
react-native SectionList实现分组列表

react-native SectionList实现分组列表

作者: sybil052 | 来源:发表于2018-07-27 17:09 被阅读34次

    SectionList组件,是高性能的分组(section)列表组件,支持下面这些常用的功能:

    • 完全跨平台。
    • 行组件显示或隐藏时可配置回调事件。
    • 支持单独的头部组件。
    • 支持单独的尾部组件。
    • 支持自定义行间分隔线。
    • 支持分组的头部组件。
    • 支持分组的分隔线。
    • 支持多种数据源结构
    • 支持下拉刷新。
    • 支持上拉加载。

    SectionList常用属性

    属性 说明 备注
    sections 用来渲染的数据,类似于FlatList中的data属性。
    initialNumToRender 指定一开始渲染的元素数量,最好刚刚够填满一个屏幕,这样保证了用最短的时间给用户呈现可见的内容。 第一批次渲染的元素不会在滑动过程中被卸载,这样是为了保证用户执行返回顶部的操作时,不需要重新渲染首批元素。
    keyExtractor 用于为给定的item生成一个不重复的key Key的作用是使React能够区分同类元素的不同个体,以便在刷新时能够确定其变化的位置,减少重新渲染的开销。若不指定此函数,则默认抽取item.key作为key值。若item.key也不存在,则使用数组下标。该函数只设置了每行(item)的key,对于每个组(section)仍然需要另外设置key。
    onRefresh 设置了此选项,会在列表头部添加一个标准的RefreshControl控件,以便实现“下拉刷新”的功能。 你需要正确设置refreshing属性。
    refreshing 在等待加载新数据时将此属性设为true,列表就会显示出一个正在加载的符号。
    ListHeaderComponent 头部组件。
    ListFooterComponent 尾部组件。
    ListEmptyComponent 当列表数据为空时渲染的组件。
    renderItem 用来渲染每一个section中的每一个列表项的默认渲染器。 可以在section级别上进行覆盖重写。必须返回一个react组件
    ItemSeparatorComponent 行与行之间的分隔线组件。不会出现在第一行之前和最后一行之后。
    renderSectionFooter 每个组的尾部组件。
    renderSectionHeader 在每个section的头部渲染。
    SectionSeparatorComponent 在每个section的顶部和底部渲染(区别于ItemSeparatorComponent,它仅在列表项之间渲染)。 它的作用是为了从视觉上把section与它上方或下方的headers区别开来,从这个意义上讲,它的作用和ItemSeparatorComponent是一样的

    废话少说,直接上代码:

    import React, {Component} from 'react';
    import {
        Platform, 
        StyleSheet, 
        Text, 
        View,
        SectionList,
        TouchableOpacity
    } from 'react-native';
    import {connect} from 'react-redux';
    import Immutable from 'immutable';
    
    // 初始化总数据
    const initArr = Immutable.fromJS([
        {date:'8月',income:'12345元',expenditure:'2520元',children:[
            {date:'8月12日',time:'18:25',orderCode:'SO18081200005',transCode:'123456',weight:'69.5Kg',money:'+241.00元',type:'收入'},
            {date:'8月10日',time:'12:01',orderCode:'SO18081000004',transCode:'123789',weight:'968.6Kg',money:'+8834.00元',type:'收入'},
            {date:'8月2日',time:'10:08',orderCode:'SO18080200003',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'},
            {date:'8月1日',time:'09:30',orderCode:'SO18080100002',transCode:'783456',weight:'169.3Kg',money:'+1526.00元',type:'收入'},
            {date:'8月1日',time:'06:47',orderCode:'SO18080100001',transCode:'456234',weight:'395.6Kg',money:'+3234.00元',type:'收入'}
        ]},
        {date:'7月',income:'16365元',expenditure:'4525元',children:[
            {date:'7月31日',time:'18:25',orderCode:'SO18071200005',transCode:'123456',weight:'69.5Kg',money:'+241.00元',type:'收入'},
            {date:'7月20日',time:'12:01',orderCode:'SO18071200004',transCode:'123789',weight:'968.6Kg',money:'+8834.00元',type:'收入'},
            {date:'7月16日',time:'10:08',orderCode:'SO18071200003',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'},
            {date:'7月12日',time:'09:30',orderCode:'SO18071200002',transCode:'783456',weight:'169.3Kg',money:'+7526.00元',type:'收入'},
            {date:'7月5日',time:'06:47',orderCode:'SO18070500001',transCode:'456234',weight:'395.6Kg',money:'+3234.00元',type:'收入'},
            {date:'7月3日',time:'10:08',orderCode:'SO18070300007',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'}
            ]},
        {date:'6月',income:'23940元',expenditure:'11560元',children:[
            {date:'6月31日',time:'18:25',orderCode:'SO18061200005',transCode:'123456',weight:'69.5Kg',money:'+241.00元',type:'收入'},
            {date:'6月20日',time:'12:01',orderCode:'SO18061200004',transCode:'123789',weight:'968.6Kg',money:'+8834.00元',type:'收入'},
            {date:'6月16日',time:'10:08',orderCode:'SO18061200003',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'},
            {date:'6月12日',time:'09:30',orderCode:'SO18061200002',transCode:'783456',weight:'169.3Kg',money:'+7526.00元',type:'收入'},
            {date:'6月5日',time:'06:47',orderCode:'SO18060500001',transCode:'456234',weight:'395.6Kg',money:'+3234.00元',type:'收入'},
            {date:'6月3日',time:'10:08',orderCode:'SO18060300007',transCode:'309876',weight:'465.6Kg',money:'-520.00元',type:'支出'}    
        ]},
        {date:'5月',income:'12005元',expenditure:'8520元',children:[
            {date:'5月31日',time:'18:25',orderCode:'SO18051200005',transCode:'123456',weight:'69.5Kg',money:'+241.00元',type:'收入'},
            {date:'5月20日',time:'12:01',orderCode:'SO18051200004',transCode:'123789',weight:'968.6Kg',money:'+8834.00元',type:'收入'},
            {date:'5月16日',time:'10:08',orderCode:'SO18051200003',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'},
            {date:'5月12日',time:'09:30',orderCode:'SO18051200002',transCode:'783456',weight:'169.3Kg',money:'+7526.00元',type:'收入'},
            {date:'5月5日',time:'06:47',orderCode:'SO18050500001',transCode:'456234',weight:'395.6Kg',money:'+3234.00元',type:'收入'},
            {date:'5月3日',time:'10:08',orderCode:'SO18050300007',transCode:'309876',weight:'465.6Kg',money:'-1520.00元',type:'支出'}    
        ]},
        {date:'4月',income:'72367元',expenditure:'7890元',children:[
            {date:'4月31日',time:'18:25',orderCode:'SO18041200005',transCode:'123456',weight:'69.5Kg',money:'+241.00元',type:'收入'},
            {date:'4月20日',time:'12:01',orderCode:'SO18041200004',transCode:'123789',weight:'968.6Kg',money:'+8834.00元',type:'收入'},
            {date:'4月16日',time:'10:08',orderCode:'SO18041200003',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'},
            {date:'4月12日',time:'09:30',orderCode:'SO18041200002',transCode:'783456',weight:'169.3Kg',money:'+7526.00元',type:'收入'},
            {date:'4月5日',time:'06:47',orderCode:'SO18040500001',transCode:'456234',weight:'395.6Kg',money:'+3234.00元',type:'收入'},
            {date:'4月3日',time:'10:08',orderCode:'SO18040300007',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'}    
        ]},
        {date:'3月',income:'12390元',expenditure:'78520元',children:[
            {date:'3月31日',time:'18:25',orderCode:'SO18031200005',transCode:'123456',weight:'69.5Kg',money:'+241.00元',type:'收入'},
            {date:'3月20日',time:'12:01',orderCode:'SO18031200004',transCode:'123789',weight:'968.6Kg',money:'+8834.00元',type:'收入'},
            {date:'3月16日',time:'10:08',orderCode:'SO18031200003',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'},
            {date:'3月12日',time:'09:30',orderCode:'SO18031200002',transCode:'783456',weight:'169.3Kg',money:'+7526.00元',type:'收入'},
            {date:'3月5日',time:'06:47',orderCode:'SO18030500001',transCode:'456234',weight:'395.6Kg',money:'+3234.00元',type:'收入'},
            {date:'3月3日',time:'10:08',orderCode:'SO18030300007',transCode:'309876',weight:'465.6Kg',money:'-22520.00元',type:'支出'},
            {date:'3月3日',time:'10:08',orderCode:'SO18030300007',transCode:'309876',weight:'465.6Kg',money:'-22520.00元',type:'支出'}        
        ]},
        {date:'2月',income:'56340元',expenditure:'9527元',children:[
            {date:'2月31日',time:'18:25',orderCode:'SO18021200005',transCode:'123456',weight:'69.5Kg',money:'-241.00元',type:'支出'},
            {date:'2月20日',time:'12:01',orderCode:'SO18021200004',transCode:'123789',weight:'968.6Kg',money:'+8834.00元',type:'收入'},
            {date:'2月16日',time:'10:08',orderCode:'SO18021200003',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'},
            {date:'2月12日',time:'09:30',orderCode:'SO18021200002',transCode:'783456',weight:'169.3Kg',money:'+7526.00元',type:'收入'},
            {date:'2月5日',time:'06:47',orderCode:'SO18020500001',transCode:'456234',weight:'395.6Kg',money:'+3234.00元',type:'收入'},
            {date:'2月3日',time:'10:08',orderCode:'SO18020300007',transCode:'309876',weight:'465.6Kg',money:'-2520.00元',type:'支出'}    
        ]},
    ]);
    
    class SectionListDemo extends Component{
        constructor(props) {
            super(props);
            this.state = {
                //改变数据的数组
                dataSource: initArr.toJS()
            };
            //for循环添加字段、删除字段
            for (let i = 0; i < this.state.dataSource.length; i++) {
                this.state.dataSource[i]['data'] = [];
                this.state.dataSource[i]['key'] = I;
                this.state.dataSource[i]['isShow'] = 'off';
                delete this.state.dataSource[i]['children'];
    
            }
            this.renderItem = this.renderItem.bind(this);
            this.renderSectionHeader = this.renderSectionHeader.bind(this);
        }
    
    
        //渲染每一个section中的每一个列表项
        renderItem(data){
            return(
                <View style={{backgroundColor:'white', flexDirection:'row',justifyContent:'space-between',paddingLeft: 5,paddingVertical: 10}}>
                    <View>
                        <View style={{flexDirection:'row'}}>
                            <Text style={styles.textStyle}>{data.item.date}</Text>
                            <Text style={styles.textStyle}>{data.item.orderCode}</Text>
                        </View>
                        <View style={{flexDirection:'row'}}>
                            <Text style={styles.textStyle}>{data.item.time}</Text>
                            <Text style={[styles.textStyle,{fontWeight:'bold'}]}>{data.item.money}</Text>
                            <Text style={styles.textStyle}>{data.item.weight}</Text>
                        </View>
                    </View>
                    <Text style={{alignSelf:'center',fontSize:16,color: '#FA5741', marginRight:15}}>{data.item.type}</Text>
                </View>
            );
        }
    
        // 根据isShow状态判断,展开改变数据源,增加数组数据,合上删除数组里的数据
        show(data){
            if (data.isShow==='off') {
                this.state.dataSource[data.key]['data'] = initArr.toJS()[data.key].children;
                this.state.dataSource[data.key]['isShow'] = 'on';
                this.setState({
                    dataSource:this.state.dataSource,
                });
            }else{
                this.state.dataSource[data.key]['data'] = [];
                this.state.dataSource[data.key]['isShow'] = 'off';
                this.setState({
                    dataSource:this.state.dataSource,
                });
            }
    
        }
        //渲染每个section的头部
        renderSectionHeader({section}){
            return(
                <TouchableOpacity 
                    style={{
                        backgroundColor:'#f1f2f3',
                        justifyContent: 'center',
                        borderBottomWidth:1,
                        borderBottomColor:'#e8e8e8',
                        padding: 5
                    }}
                    onPress={()=>{this.show(section)}}>
                    <View>
                        <Text style={{fontSize:18,padding:5}}>{section.date}</Text>
                        <View style={{flexDirection:'row'}}>
                            <Text style={{fontSize:15,padding:5}}>收入:{section.income}</Text>
                            <Text style={{fontSize:15,marginLeft:25,padding:5}}>支出:{section.expenditure}</Text>
                        </View>
                    </View>
                </TouchableOpacity>
            );
        }
        renderItemSeparator() {
            return (
                <View style={styles.divideLine} ></View>
            );
        }
        renderSectionSeparator() {
            return (
                <View style={{height:1,backgroundColor:'red'}} />
            );
        }
        
        extraUniqueKey(item,index){
            return index+item;
        }
        render() {
            console.log('========'+JSON.stringify(this.state.dataSource));
            return (
                <View style={styles.container}>
                    <SectionList
                        sections={this.state.dataSource}// 数据源
                        renderItem={this.renderItem} // 渲染每一个section中的每一个列表项 
                        keyExtractor = {this.extraUniqueKey}// 生成一个不重复的key
                        renderSectionHeader={this.renderSectionHeader}// 渲染每个section的头部
                        scrollEnabled={true}//默认是true,false禁止滚动
                        ItemSeparatorComponent={this. renderItemSeparator.bind(this)}// item分隔线组件
                        SectionSeparatorComponent={this.renderSectionSeparator.bind(this)} // section分隔线组件 
                    >
                    </SectionList>
                </View>
            );
        }
    }
    
    const styles = StyleSheet.create({
        container: {
            flex: 1,
            backgroundColor: '#FFF',
        },
        header:{
            height: 64,
            paddingTop: 14,
            backgroundColor:'white',
            flexDirection:'row',
        },
        textStyle:{
            fontSize: 15,
            marginLeft: 5,
            padding: 5,
        },
        divideLine:{
            height: 1,
            backgroundColor: '#e8e8e8',
        },
    });
    
    const mapStateToProps = state => ({
        nav: state.nav,
    })
    
    const mapDispatchToProps = (dispatch) => {
        return {
            dispatch
        }
    }
    
    export default connect(mapStateToProps, mapDispatchToProps)(SectionListDemo)
    

    上效果图:

    QQ20180726-164452.gif

    相关文章

      网友评论

      • long2016:我加了分组间隔之后, 在 section 的 header与 item 之间出现了间隔, 怎么回事?

      本文标题:react-native SectionList实现分组列表

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