React Native 加載多類型佈局的實現——分類列表SectionList的封裝

目標:簡化及規範SectionList的使用。
實現:基於SectionList的封裝。

適用的情況
      普通的線佈局+列表/分類列表+普通線程佈局+列表/分類列表。如下圖:

      如上圖中1、2、3、4(評論回覆模塊)就是普通的線布+列表+普通線程佈局+分類列表的形式。

封裝後的BaseSectionList.js類,如下:

/**
 * @desc
 * @author MaRui
 */
import {
    View,
    StyleSheet,
    SectionList
} from 'react-native';
import React, {Component} from "react";
// [
//     {data: [], layoutType: 0, title: {}}
// ]
export default class BaseSectionList extends Component {
    constructor(props) {
        super(props);
        this.state = {
            dataList: []
        };
    }

    componentDidMount() {

        if (!this.props.data) {
            return
        }

        let newDataList = [];
        if (this.props.data && React.Children.toArray(this.props.children).length !== this.props.length) {
            this.setState({dataList: []});
            return;
        }
        for (let i = 0; i < this.props.data.length; i++) {
            if (this.props.data[i].length === 0) {
                newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
                continue
            }
            for (let j = 0; j < this.props.data[i].length; j++) {
                let datumElement = this.props.data[i][j];
                if (datumElement) {
                    let title = this.props.data[j].title;
                    let data = this.props.data[j].content;
                    newDataList.splice(newDataList.length, 0, {title: title, data: data, layoutType: i});
                } else {
                    newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
                }
            }
        }
        this.setState({dataList: newDataList});
    }

    componentWillReceiveProps(nextProps) {

        if (!this.props.data) {
            return
        }

        let isRefresh = false;
        if (this.props.data !== nextProps.data && this.props.data.length !== nextProps.data.length) {
            isRefresh = true;
        } else {
            for (let i = 0; i < this.props.data.length; i++) {
                if (this.props.data[i] !== nextProps.data[i]) {
                    isRefresh = true;
                    break;
                }
            }
        }

        if (isRefresh) {
            let newDataList = [];
            for (let i = 0; i < nextProps.data.length; i++) {
                if (nextProps.data[i].length === 1 && nextProps.data[i][0].baseSectionListType === 'baseSectionListContent') {
                    newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
                    continue
                }
                for (let j = 0; j < nextProps.data[i].length; j++) {
                    let datumElement = nextProps.data[i][j];
                    if (datumElement) {
                        let title = datumElement.title;
                        let data = datumElement.content;
                        newDataList.splice(newDataList.length, 0, {title: title, data: data, layoutType: i});
                    } else {
                        newDataList.splice(newDataList.length, 0, {title: {}, data: [], layoutType: i});
                    }
                }
            }

            this.setState({dataList: newDataList}, () => {
                // alert('值:'+JSON.stringify(this.state.dataList)+'\n個數:'+this.state.dataList.length);
            });
        }
    }

    //分組創建的cell
    cellView = (data) => {
        if (data && this.props.children) {
            if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCell) {
                return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCell(data)
            }

            // 建議數據少時使用
            if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCellIsShow && !this.props.itemIsShow) {
                return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderCellIsShow(data)
            }
        }
        return <View/>
    };

    //列表分組的header
    headerView = (data) => {
        if (data && this.props.children) {
            if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeader) {
                return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeader(data)
            }

            // 建議數據少時使用
            if (React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeaderIsShow && !this.props.itemIsShow) {
                return React.Children.toArray(this.props.children)[data.section.layoutType].props.renderHeaderIsShow(data)
            }
        }
        return <View/>
    };

    extraUniqueKey = (item, index) => {
        return index + item;
    };

    render() {

        return (
            <View style={[{flex: 1}]}>
                <View style={styles.container}>
                    <SectionList
                        {...this.props}
                        sections={this.state.dataList}
                        renderItem={this.cellView}
                        keyExtractor={this.extraUniqueKey}
                        renderSectionHeader={this.headerView}
                        scrollEnabled={true}
                        stickySectionHeadersEnabled={false}
                    />
                </View>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    container: {
        flex: 1
    },

});

主要實現了,不用給每一塊的數據做標記就可以實現分類列表的顯示。之後補充封裝代碼的解析

BaseSectionList的使用,實現上圖如下:

佈局:

<BaseSectionList
         // 此處可以添加設置SectionList的任何屬性,比如頭部佈局、底部佈局、分割線等等
         style={{flex: 1}}
         data={this.props.data}
         ListHeaderComponent={this.headerRender}>
         
         <View
        	renderCell={this.renderListCell}/>
        	
        <View
        	renderHeader={this.renderTitle}/>
        	
         <View
            renderHeader={this.renderComment}
            renderCell={this.renderReply}/>
            
</BaseSectionList>

        其中佈局1爲普通佈局可以設置爲ListHeaderComponent。此後,每一塊佈局都添加一個子view,234三塊佈局就添加三個標籤“<View/>”。具體設置:比如佈局2是列表則添加一個子view只設置renderCell,renderCell是list的item的佈局。佈局3是一個線性佈局,可設置renderHeader即可。佈局4是一個二級列表即分類列表,需要同時設置renderHeader和renderCell分別代表分類標題佈局和分類列表item的佈局。

數據結構:

let data = [];
data.splice(0, 0, [{title: {}, content: []}]);
data.splice(1, 0, [{baseSectionListType: 'baseSectionListContent'}]);
data.splice(2, 0, [{title: {'課程'}, content: ['語文', '數學', '英語']},
                   {title: {'班級'}, content: ['一班', '二班', '三班']},
                   {title: {'老師'}, content: ['張老師', '王老師', '李老師']}]);

        傳輸的數據是一個數組。上邊添加的三個佈局,則數組的長度必須是3。每個元素也必須是數組,該例子的格式即是 [ [ ], [ ], [ ] ]。下面說每個元素的填充。分以下三種情況:

  • 模塊是列表,比如:佈局2
            元素是一個數組,長度是1。[{title: {}, content: []}]。title設置爲空對象,content傳數組即該塊列表對應的數組。
  • 模塊是線性佈局,比如:模塊3
            元素是一個數組,長度是1, 元素是{baseSectionListType: ‘baseSectionListContent’}。這個是固定不變的,實際只起到佔位符的作用。該模塊的數據自己管理。
  • 模塊是分類列表,比如:模塊4
            元素是一個數組,數組的長度是分類列表的類的個數,比如有3類[{title: {}, content: []}, {title: {}, content: []}, {title: {}, content: []}]。title是標題對應的數據,content傳數組即該塊列表對應的數組。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章