react-native SectionList实现分组列表

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
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,843评论 6 502
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,538评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,187评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,264评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,289评论 6 390
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,231评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,116评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,945评论 0 275
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,367评论 1 313
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,581评论 2 333
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,754评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,458评论 5 344
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,068评论 3 327
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,692评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,842评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,797评论 2 369
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,654评论 2 354

推荐阅读更多精彩内容