天天看點

react-native系列(9)元件篇:最優清單顯示方案FlatList和SectionListFlatList清單SectionList清單組

FlatList清單

FlatList是一個高性能的清單元件。原理是:隻負責渲染目前可見的清單項,對于不可見的項将不會渲染因為可見的項總是有限的,當一個項被劃出螢幕後,被滑出項的容器将會成為新滑入的項的容器而不會重新再渲染一個,是以性能要比ScrollView和ListView元件高。

下面是一個原理簡圖:

react-native系列(9)元件篇:最優清單顯示方案FlatList和SectionListFlatList清單SectionList清單組

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;
           

效果:

react-native系列(9)元件篇:最優清單顯示方案FlatList和SectionListFlatList清單SectionList清單組

小貼士:由于加載的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;
           

效果:

react-native系列(9)元件篇:最優清單顯示方案FlatList和SectionListFlatList清單SectionList清單組

繼續閱讀