RN SectionList 使用Demo

前言

SectionList 是最为常用的控件。此Demo包含数据分组展示、编辑全选、单选,上拉刷新、下拉加载更多等,如下效果。

效果演示

在这里插入图片描述

代码


import React from 'react';

import {
    Dimensions,
    Image, Platform, SectionList, StatusBar, Text, TouchableOpacity, View
} from 'react-native';

import LinearGradient from "react-native-linear-gradient";
import ImageButton from "miot/ui/ImageButton";

const tag = "SectionListDemo";

let screenHeight = Dimensions.get('window').height;
let screenWidth = Dimensions.get('window').width;
let containerHeight = (StatusBar.currentHeight || 0) + 65;

export default class SectionListDemo extends React.Component{

    constructor(props) {
        super(props);

        let sections = [
            {title: "09:00-10:00", data: [{dateTime: "09:01"}]},
            {title: "10:00-11:00", data: [{dateTime: "10:01"}, {dateTime: "10:02"}]},
            {title: "11:00-12:00", data: [{dateTime: "11:01"}, {dateTime: "11:02"}, {dateTime: "11:03"}]},
            {title: "12:00-13:00", data: [{dateTime: "12:01"}, {dateTime: "12:02"}, {dateTime: "12:03"}, {dateTime: "12:04"}]},
            {title: "13:00-14:00", data: [{dateTime: "13:01"}, {dateTime: "13:02"}, {dateTime: "13:03"}, {dateTime: "13:04"}]},
        ]

        this.state = {
            sections: sections,
            isRefreshing: false,
            showFoot: 0,

            currentItemTag:undefined,
            isSelectMode:false,
        }

        this.requestData = false;
        this.hasMoreData = true;

        this.requestMoreTimes = 0;//用来模拟器请求更多

        //this.dataToGroup();
    }

    static navigationOptions = ({ navigation }) => {
        return {
            headerTransparent: true,
            header: null
        }
    };

    _renderTitleView() {

        StatusBar.setBarStyle('dark-content');

        if (Platform.OS == 'android') {
            StatusBar.setTranslucent(true);
        }

        const containerStyle = {
            position: "absolute",
            top: 0,
            backgroundColor: "#00000000",
            height: containerHeight,
            width: "100%",
            display: "flex",
            flexDirection: "row",
            alignItems: "center",//垂直居中
            paddingLeft: 9,
            paddingRight: 9,
        }

        const textContainerStyle = {
            flexGrow: 1,
            alignSelf: 'stretch',//控制自己填充满父类的高度
            display: "flex",
            flexDirection: "column",
            justifyContent: 'center',
            alignItems: 'stretch',//控制子类填充满本身的宽度
            marginHorizontal: 5,
        }

        let screenWidth = Dimensions.get('window').width;

        const titleTextStyle = {
            fontSize: 16,
            width: screenWidth - 110,
            fontFamily: 'D-DINCondensed-Bold',
            textAlignVertical: 'center',
            textAlign: 'center'
        }

        const darkTitleColor = '#000000'; //标题颜色
        const titleColor = {color: darkTitleColor};
        let iconSize = 40;

        let isSelectMode = this.state.isSelectMode;

        let title;
        let isSelectAll = false;
        if(isSelectMode){
            let selectedCount = 0;
            let notSelectedCount = 0;
            let sections = this.state.sections;
            for (let section of sections) {
                for (let item of section.data) {
                    if(item.isSelected){
                        selectedCount++;
                    }else {
                        notSelectedCount++;
                    }
                }
            }
            isSelectAll = (notSelectedCount === 0);
            title = "选择了" + selectedCount + "个";
        }else {
            title = "SectionListDemo演示"
        }

        const iconBack = require("../../resources/Images/icon_back_black.png");
        const iconCancel = require("../../resources/Images/icon_cancle_black.png")
        const iconSelect = require("../../resources/Images/icon_select_black.png")
        const icon_select_active = require("../../resources/Images/icon_select_active.png")
        const iconEdit = require("../../resources/Images/icon_edit_black.png")

        const leftIcon = isSelectMode ? iconCancel : iconBack;
        const rightIcon = isSelectMode ? (isSelectAll ? icon_select_active : iconSelect) : iconEdit;

        const leftIconData = {
            source: leftIcon,
            onPress: (() => {
                if(isSelectMode){
                    //退出编辑
                    this.setState({isSelectMode:false})
                    this._doSectionSelectAll(false);
                }else {
                    //退出页面
                    this.props.navigation.goBack();
                }

            })
        }

        const rightIconData = {
            source: rightIcon,
            onPress: (()=>{

                if(isSelectMode){
                    //全选处理
                    this._doSectionSelectAll(!isSelectAll);
                }else {
                    //进入编辑
                    this.setState({isSelectMode:true,currentItemTag:undefined});
                }
            })
        }

        return (
            <View style={{width: "100%", height: 75}}>
                <StatusBar backgroundColor={'transparent'}/>
                <LinearGradient
                    colors={['#00000000', '#00000000']}
                    style={containerStyle}>
                    <View
                        style={{width: iconSize, height: iconSize, position: "relative"}}>

                        <ImageButton
                            style={{width: iconSize, height: iconSize, position: "absolute", marginLeft: 0}}
                            source={leftIconData.source}
                            onPress={leftIconData.onPress}
                        />
                    </View>


                    <View style={textContainerStyle}>
                        <Text
                            numberOfLines={1}
                            style={[titleTextStyle, titleColor]}
                        >
                            {title}
                        </Text>

                    </View>


                    <View
                        style={{width: iconSize, height: iconSize, position: "relative"}}>

                        <ImageButton
                            style={{width: iconSize, height: iconSize, position: "absolute"}}
                            source={rightIconData.source}
                            onPress={rightIconData.onPress}
                        />
                    </View>

                </LinearGradient></View>
        );
    }

    render() {
        return (
            <View style={{
                backgroundColor: "white",
                width: '100%',
                height: '100%',
                display: "flex"
            }}>
                {this._renderTitleView()}

                {this._renderSectionList()}

                {this._renderBottomDeleteView()}
            </View>
        )
    }



    _renderSectionList(){
        return (
            <View style={{height:(screenHeight-containerHeight)}}>
                {/*height指定解决onEndReached多次触发问题*/}

                <SectionList
                    style={{marginHorizontal:5}}
                    renderSectionHeader={({ section: section }) => this._renderSectionHeader(section)}
                    renderItem={({ item, index ,section}) => this._renderItem(item, index,section)}
                    sections={this.state.sections}
                    extraData={this.state}
                    keyExtractor={(item, index) => index}
                    stickySectionHeadersEnabled={false}
                    scrollIndicatorInsets={{right: 1}/*ios 滚动条异常滚动条居中解决方案*/}

                    contentContainerStyle={{flexDirection: 'row',flexWrap: 'wrap'}}
                    onRefresh={this._onRefresh.bind(this)}
                    refreshing={this.state.isRefreshing}
                    onEndReached={this._onEndReached.bind(this)}
                    onEndReachedThreshold={0.1}
                    ListFooterComponent={this._renderFooter.bind(this)}
                />
            </View>
        )
    }

    _renderSectionHeader(section) {

        //console.log(tag,"_renderSectionHeader",section)

        let title = section.title;

        let isSelectMode = this.state.isSelectMode;

        let isSelectAll = true;
        let items = section.data;
        for (let item of items) {
            if(!item.isSelected){
                isSelectAll = false;
                break;
            }
        }

        let selectAllTxt = isSelectAll?"全不选":"全选";

        return (
            <View style={{flexDirection: 'row', height:30,width: Dimensions.get('window').width,marginVertical:5,paddingLeft:2,alignItems:"center"}}>
                <Text style={{color:"#7F7F7F",fontSize:15}}>
                    {title}
                </Text>
                {
                    isSelectMode ?
                        <TouchableOpacity style={{position: "absolute", right: 20,backgroundColor:'#E5E5E5',borderRadius:10,paddingVertical:5,paddingHorizontal:10}}
                                          onPress={()=>{
                                              this._doSectionHeaderSelectAll(title,!isSelectAll);
                                          }}>
                            <Text style={{color:"#000000",fontSize:15,fontWeight: 'bold'}}>
                                {selectAllTxt}
                            </Text>
                        </TouchableOpacity>:null
                }
            </View>
        )
    }

    _renderItem(item, index,section) {

        //console.log(tag,"_renderItem",item,index,section);

        let marginHorizontal = 3.5;

        let screenWidth = Dimensions.get('window').width;
        let containerWidth = (screenWidth - 11 -marginHorizontal * 6) / 3;

        let dateTimeTxt = item.dateTime;
        let itemImgSource = require("../../resources/Images/item_icon.png") ;

        let currentItemTag = item.dateTime;

        let isSelectMode = this.state.isSelectMode;
        let isSelected = item.isSelected;
        let isSelectedImg = isSelected ? require("../../resources/Images/icon_selected.png") : require("../../resources/Images/icon_unselected.png");


        let curItemBackground = {
            borderColor: "#32BAC0",
            borderWidth: 1.5,
            borderRadius:6
        }
        return (
            <TouchableOpacity
                style={[{ width: containerWidth, height: 85, marginBottom: 10, marginLeft: marginHorizontal, marginRight: marginHorizontal }, this.state.currentItemTag == item.dateTime ? curItemBackground : null]}
                onPress={()=>{
                    if(!isSelectMode){
                        this.setState({currentItemTag:currentItemTag})
                    }else {
                        let sections = this.state.sections;
                        for (let section of sections) {
                            for (let item of section.data) {
                                if(item.dateTime === currentItemTag){
                                    item.isSelected = !item.isSelected;
                                }
                            }
                        }

                        this.setState({sections:sections})
                    }
                }}
            >

                <View style={{ width: "100%", height: 60, marginBottom: 3, position: "relative" }} >
                    <Image style={{ width: "100%", height: "100%", borderRadius: 5 }}
                           source={itemImgSource}
                    />

                    {
                        isSelectMode ?
                            <Image style={{ width: 18, height: 18, position: "absolute", bottom: 4, right: 4 }}
                                   source={isSelectedImg}
                            /> :null
                    }
                </View>
                <View style={{ display: "flex", flexDirection: "row", alignItems: "center" }}>
                    <Text  style={{ marginLeft: 4, marginRight: 4, fontSize: 11, color: "#000000" }}>
                        {dateTimeTxt}
                    </Text>
                </View>


            </TouchableOpacity>
        );
    }

    // 头部刷新
    _onRefresh = () => {
        if(this.state.isSelectMode){
            return;
        }

        this.setState({isRefreshing:true})

        //模拟去服务器拉取数据
        setTimeout(()=>{
            this.setState({isRefreshing:false})
        },5000)
    };

    _onEndReached = () => {

        if(this.state.isSelectMode){
            return;
        }

        console.log(tag,"_onEndReached","showFoot=",this.state.showFoot,"hasMoreData=",this.hasMoreData,"requestData=",this.requestData);

        // 如果是正在加载中或没有更多数据了,正在请求数据,则返回
        if (this.state.showFoot != 0 || this.requestData) {
            this.onEndReachedFlag = false;
            return;
        }

        if (this.hasMoreData) {
            this.setState({showFoot: 2});

            //模拟去服务器拉取更多数据
            this.requestData = true;
            this.requestMoreTimes++;

            setTimeout(()=>{
                this.requestData = false;
                console.log(tag,"_onEndReached","requestData=",this.requestData);

                let sections = this.state.sections;
                sections.push({title:"x"+this.requestMoreTimes+":xx",data:[{dateTime: "x"+this.requestMoreTimes+":01"}, {dateTime: "x"+this.requestMoreTimes+":02"}]})
                this.setState({sections:sections});

                if(this.requestMoreTimes>=5){
                    this.hasMoreData = false;
                }else {
                    this.hasMoreData = true;
                }

                this.setState({showFoot: this.hasMoreData ? 0 : 1});

            },3000)

        } else {
            this.setState({showFoot: 1});
        }

        this.onEndReachedFlag = false;
    }

    _renderFooter() {
        //console.log(tag,"_renderFooter","showFoot=",this.state.showFoot)

        if (this.state.showFoot === 1) {
                return (
                    <View style={{height: 30,width:screenWidth, justifyContent: 'center',alignItems: 'center',}}>
                        <Text style={{flex:1,textAlignVertical: 'center',textAlign: 'center',color: '#999999', fontSize: 14}}>
                            没有可加载的数据
                        </Text>
                    </View>
                )
        } else if (this.state.showFoot === 2) {
            return (
                <View style={{
                    width:screenWidth,
                    flexDirection: 'row',
                    height: 30,
                    justifyContent: 'center',
                    alignItems: 'center'
                }}>
                    <Text style={{flex:1,textAlignVertical: 'center',textAlign: 'center'}}>正在加载数据...</Text>
                </View>
               );
        } else if (this.state.showFoot === 0) {
            return (
                <View style={{height: 0,width:screenWidth}}>
                    <Text>{/* 正常情况:不显示加载中 或 没有可加载的数据 */}</Text>
                </View>
            );
        }
    }

    _doSectionSelectAll(isSelectAll){
        let sections = this.state.sections;
        for (let section of sections) {
            for (let item of section.data) {
                item.isSelected = isSelectAll;
            }
        }
        this.setState({sections:sections})
    }

    _doSectionHeaderSelectAll(sectionTitle,isSelectAll){
        let sections = this.state.sections;
        for (let section of sections) {
            if(sectionTitle === section.title){
                for (let item of section.data) {
                    item.isSelected = isSelectAll;
                }
            }
        }
        this.setState({sections:sections})
    }

    _renderBottomDeleteView() {
        if (!this.state.isSelectMode) {
            return;
        }

        return (
            <View style={{ width: "100%",height: 70,display: "flex",justifyContent: "center",alignItems: "center" }}>
                <View style={{width:"100%",height:1,backgroundColor: "#e5e5e5"}}></View>
                <TouchableOpacity
                    style={{ width: 50, display: "flex", alignItems: "center" }}
                    onPress={() => {

                        let delItems = [];

                        let sections = this.state.sections;
                        for (let section of sections) {
                            for (let item of section.data) {
                                if(item.isSelected){
                                    delItems = delItems.concat(item);
                                }
                            }
                        }

                        console.log(tag,"delItems=",delItems)
                    }}
                >
                    <Image
                        style={{ width: 35,height: 35 }}
                        source={require("../../resources/Images/icon_delete_photo_black.png")} />
                    <Text style={{ color: "#000000",fontSize: 11 }} >
                        {"删除"}
                    </Text>
                </TouchableOpacity>
            </View>
        );
    }

    dataToGroup(){
        let data1 = [
        {dateTime: "09:01"},
        {dateTime: "09:02"},
        {dateTime: "10:01"},
        {dateTime: "10:02"},
        {dateTime: "11:01"},
        {dateTime: "11:02"},
        {dateTime: "11:03"},
        {dateTime: "12:01"},
        {dateTime: "12:02"},
        {dateTime: "12:03"}]

        //倒序
        let data2 = data1.sort(function(a,b){
            return parseInt(b.dateTime.replace(/:/g,''))
                -parseInt(a.dateTime.replace(/:/g,''));
        })

        console.log("data1=",data1,"data2=",data2);

        let groupData=[]
        for (let i = 0; i < data2.length; i++) {
            let hour = (data2[i].dateTime.split(":"))[0];

            let title= hour;
            let data = data2[i];

            //遍历是否存在title
            if(groupData.length===0){
                groupData.push({title:title,data:[data]})
            }else {
                let isExist = false;
                for (let i = 0; i < groupData.length; i++) {
                    if(title === groupData[i].title){
                        groupData[i].data.push(data);
                        isExist = true;
                        break;
                    }
                }

                if(!isExist){
                    groupData.push({title:title,data:[data]})
                }
            }
        }

        console.log("groupData=",groupData);
    }
}

SectionList【sections】数据格式组装

    dataToGroup(){
        let data1 = [
        {dateTime: "09:01"},
        {dateTime: "09:02"},
        {dateTime: "10:01"},
        {dateTime: "10:02"},
        {dateTime: "11:01"},
        {dateTime: "11:02"},
        {dateTime: "11:03"},
        {dateTime: "12:01"},
        {dateTime: "12:02"},
        {dateTime: "12:03"}]

        //倒序
        let data2 = data1.sort(function(a,b){
            return parseInt(b.dateTime.replace(/:/g,''))
                -parseInt(a.dateTime.replace(/:/g,''));
        })

        console.log("data1=",data1,"data2=",data2);

        let groupData=[]
        for (let i = 0; i < data2.length; i++) {
            let hour = (data2[i].dateTime.split(":"))[0];

            let title= hour;
            let data = data2[i];

            //遍历是否存在title
            if(groupData.length===0){
                groupData.push({title:title,data:[data]})
            }else {
                let isExist = false;
                for (let i = 0; i < groupData.length; i++) {
                    if(title === groupData[i].title){
                        groupData[i].data.push(data);
                        isExist = true;
                        break;
                    }
                }

                if(!isExist){
                    groupData.push({title:title,data:[data]})
                }
            }
        }

        console.log("groupData=",groupData);
    }

总结

工作中常用,做个云备份,方便查看、总结、拓展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Crazy程序猿2020

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值