學(xué)習(xí)總結(jié) ~ (三)React Native 初學(xué) 之 sectionList

目的

今天我要實現(xiàn)一個 類似于 iOS 開發(fā)中帶有分組的colllectionView 樣式的布局, 每個section都要有個組頭。

首先我們要先決定要使用什么控件。ScrollViewListView/FlatList還有SectionList都是可以選擇的。

  • ScrollView 會把所有子元素一次性全部渲染出來。使用上最簡單。但是如果你有一個特別長的列表需要顯示,可能會需要好幾屏的高度。這時就會占用很大的內(nèi)存去創(chuàng)建和渲染那些屏幕以外的JS組件和原生視圖,性能上也會有所拖累。
  • ListView 更適用于長列表數(shù)據(jù)。它會惰性渲染子元素,并不會立即渲染所有元素,而是優(yōu)先渲染屏幕上可見的元素。
  • FlatList 是0.43版本開始新出的改進版的ListView,性能更優(yōu),但是官方說現(xiàn)在可能不夠穩(wěn)定,尚待時間考驗。但是它不能夠分組/類/區(qū)(section)。
  • SectionList 也是0.43版本推出的, 高性能的分組列表組件。但是它不支持頭部吸頂懸浮的效果,但是也不要傷心,官方在下一個版本開始就可以支持懸浮的section頭部啦 ??。
    好啦, 綜上所訴我選擇使用SectionList ,現(xiàn)在開始干活吧 ??

首先


首先第一步我們先把要顯示的樣式寫好作為子控件, 把數(shù)據(jù)源整理好。

例一、
<SectionList
  renderItem={({item}) => <ListItem title={item.title} />}
  renderSectionHeader={({section}) => <H1 title={section.key} />}
  sections={[ // 不同section渲染相同類型的子組件
    {data: [...], key: ...},
    {data: [...], key: ...},
    {data: [...], key: ...},
  ]}
/>

例二、
<SectionList
  sections={[ // 不同section渲染不同類型的子組件
    {data: [...], key: ..., renderItem: ...},
    {data: [...], key: ..., renderItem: ...},
    {data: [...], key: ..., renderItem: ...},
  ]}
/>

sections 就是我們的數(shù)據(jù)源,每一個data 就是我們要用的item, renderItem就是你要顯示的子控件哦。如果你每個組都復(fù)用一個子組件那就按照例一的結(jié)構(gòu), 如果你想要不同的組返回不同樣式的子組件那就按照例二的結(jié)構(gòu)返回不同的renderItem即可。
***這里提個醒, key一定要有, 不同的section 要設(shè)置不同的key才會渲染相應(yīng)的section, 如果你key值都相同, 那可能會出現(xiàn)只顯示一組數(shù)據(jù)的情況哦~ ***

下面來看看我的代碼:
 <SectionList
                        renderItem={this._renderItem}
                        renderSectionHeader={this._renderSectionHeader}
                    sections={[ // 不同section渲染相同類型的子組件
                            { data: [{ title: this.state.appModel[0] }], key: this.state.groupsModel[0].title },
                            { data: [{ title: this.state.appModel[1] }], key: this.state.groupsModel[1].title },
                            { data: [{ title: this.state.appModel[2] }], key: this.state.groupsModel[2].title },
                            { data: [{ title: this.state.appModel[3] }], key: this.state.groupsModel[3].title },
                        ]}
                    />
1.png

這樣有了最基礎(chǔ)的樣式, 四組縱向的列表, 但是我要橫向的, 于是我要設(shè)置他的樣式啦。

接下來


這里我添加兩個屬性:

  contentContainerStyle={styles.list}//設(shè)置cell的樣式
  pageSize={4}  // 配置pageSize確認網(wǎng)格數(shù)量

const styles = StyleSheet.create({
    list: {
        //justifyContent: 'space-around',
        flexDirection: 'row',//設(shè)置橫向布局  
        flexWrap: 'wrap',  //設(shè)置換行顯示
        alignItems: 'flex-start',
        backgroundColor: '#FFFFFF'
    },
});

好啦, 讓我們來看看效果。


2.png

??這是什么鬼???
為什么它的組頭也在左邊 , 并且他的其他組數(shù)據(jù)都橫著了, 對于小白我來說只有大寫的懵~。不知道你們有沒有遇到這種情況, 是什么原因?qū)е碌模?我很是困惑啊, 當(dāng)我把

          renderSectionHeader={this._renderSectionHeader}

這行代碼注掉的時候, 它的顯示是正常的...


3.png

這就尷尬了...
它的每一個小方塊是一個item,達不到我要的效果啊, 于是我決定換個思路, 誰讓我是打不死的小白呢??

重新來


我決定讓每個section是一個item。在每個item上創(chuàng)建多個可點擊的板塊。show time ~ ~

 _renderItem = ({ item}) => (

        <View  style={styles.list}>
            {
                item.map((item, i) => this.renderExpenseItem(item, i))
            }
        </View>

    );

    renderExpenseItem(item, i) {

        return <TouchableOpacity key={i} onPress={() => this._pressRow(item)} underlayColor="transparent">
            <View style={styles.row}>
                <CellView source={item.img}></CellView>
            </View>
        </TouchableOpacity>;
    }

    _renderSectionHeader = ({ section }) => (
        <View style={{ flex: 1, height: 25 }}>
            <Text style={styles.sectionHeader} >{section.key}</Text>
        </View>
    );

    render() {
        return (
            <View style={{ flex: 1 }}>
                <Text style={styles.navigatorStyle}> 發(fā)現(xiàn) </Text>
                <View style={{ flex: 1, backgroundColor: '#F7F6F8' }}>

                    <SectionList
                        renderItem={this._renderItem}
                        renderSectionHeader={this._renderSectionHeader}
                        showsVerticalScrollIndicator={false}
                        sections={ // 不同section渲染相同類型的子組件
                            this.state.dataSource
                        }
                    />
                </View>
            </View>
        );
    }
}

const styles = StyleSheet.create({
    list: {
        //justifyContent: 'space-around',
        flexDirection: 'row',
        flexWrap: 'wrap',
        alignItems: 'flex-start',
        backgroundColor: '#FFFFFF'
    },
    row: {
        backgroundColor: '#FFFFFF',
        justifyContent: 'center',
        width: (ScreenWidth - 1) / 4,
        height: (ScreenWidth - 1) / 4,
        alignItems: 'center',
    },
    sectionHeader: {
        marginLeft: 10,
        padding: 6.5,
        fontSize: 12,
        color: '#787878'
    },
});

這里的dataSource 我是在之前數(shù)據(jù)的基礎(chǔ)上又包了一層[],然后在renderItem里做了map映射, 這樣每個renderItem上返回了每一組我所需要的子組件。快來看看我的變化吧??


4.png

腫么樣, 達到效果了吧, 但是你有沒有發(fā)現(xiàn) 底部為啥是黃色的?,我可沒有去設(shè)置這么丑的顏色哦,其實它是提醒我們有不完美的地方, 下面就讓我們解決一下這個不完美吧 。

最后解決問題


warning.png

最后讓我們來解決問題。它警告我們每個item 要有不同的key ,還記不記得我上面的提醒,我也犯這個錯誤了。

  • 默認情況下每行都需要提供一個不重復(fù)的key屬性。你也可以提供一個keyExtractor函數(shù)來生成key。
把這個屬性添加到      <SectionList/> 里面
       keyExtractor = {this._extraUniqueKey}   

         _extraUniqueKey(item ,index){
      return "index"+index+item;
}  

這是每個item要設(shè)置key, 同樣每個子控件也不能放過, 一定要設(shè)置它的key, 要不然這個屎黃色一直伴著你 多煩~~~

最后看一下我最終的代碼吧!

var Dimensions = require('Dimensions');//獲取屏幕的寬高
var ScreenWidth = Dimensions.get('window').width;
var ScreenHeight = Dimensions.get('window').height;

// const AnimatedSectionList = Animated.createAnimatedComponent(SectionList);// 這個是創(chuàng)建動畫
export default class Explore extends Component {

    constructor(props) {
        super(props);
        this.state = {
            appModel: null,
            groupsModel: null,
            dataSource: null,
        }
    }

    //Component掛載完畢后調(diào)用
    componentDidMount() {
        this.fetchData();
    }

    async fetchData() {
        try {
            let model = await NetFetch.post(_req_url_path, {

            });
            let apps = model.apps;
            let groups = model.groups;

            let data = [];
            for (let i = 0; i < model.groups.length; i++) {
                let row = [];
                for (let j = 0; j < model.apps.length; j++) {

                    if (model.groups[i].appIds.indexOf(model.apps[j].appId) >= 0) {
                        row.push(model.apps[j]);
                    }
                }

                data.push({ data: [row], key: model.groups[i].title });
            }
// 這里我重組了一下數(shù)據(jù)結(jié)構(gòu), 看沒看見我在row外面又包了一層, 為了我循環(huán)創(chuàng)建每個section的子組件。

            this.setState({
                appModel: model.apps,
                groupsModel: model.groups,
                dataSource: data
            });

        } catch (error) {
            alert(error.msg);
        }
    }

    _renderItem = ({ item}) => (

        <View  style={styles.list}>
            {
                item.map((item, i) => this.renderExpenseItem(item, i))
            }
        </View>

    );

    renderExpenseItem(item, i) {

        return <TouchableOpacity key={i} onPress={() => this._pressRow(item)} underlayColor="transparent">
            <View style={styles.row}>
                <CellView source={item.img}></CellView>
            </View>
        </TouchableOpacity>;
    }


    _renderSectionHeader = ({ section }) => (
        <View style={{ flex: 1, height: 25 }}>
            <Text style={styles.sectionHeader} >{section.key}</Text>
        </View>
    );

    _listHeaderComponent() {
        return (
            <HeaderView integral={0}></HeaderView>
        );
    }

    _listFooterComponent() {
        return (
            <Text style={[styles.remark]}>*預(yù)期收益非平臺承諾收益,市場有風(fēng)險,投資需謹慎</Text>
        );
    }

    _pressRow(item) {
        this.props.navigator.pushTo(item.go)
    } 

    _extraUniqueKey(item ,index){
        return "index"+index+item;
   } 

    render() {
        if (!this.state.dataSource) {
            return (
                <View></View>
            );
        }

        return (
            <View style={{ flex: 1 }}>

                <Text style={styles.navigatorStyle}> 發(fā)現(xiàn) </Text>

                <View style={{ flex: 1, backgroundColor: '#F7F6F8' }}>

                    <SectionList
                        contentInset={{top:0,left:0,bottom:49,right:0}}// 設(shè)置他的滑動范圍
                        renderItem={this._renderItem}
                        ListFooterComponent={this._listFooterComponent}
                        ListHeaderComponent={this._listHeaderComponent}
                        renderSectionHeader={this._renderSectionHeader}
                        showsVerticalScrollIndicator={false}
                        keyExtractor = {this._extraUniqueKey}// 每個item的key
                        // contentContainerStyle={styles.list}
                        // horizontal={true}
                        // pageSize={4}  // 配置pageSize確認網(wǎng)格數(shù)量
                        sections={ // 不同section渲染相同類型的子組件
                            this.state.dataSource
                        }

                    />
                </View>
            </View>
        );
    }

}


const styles = StyleSheet.create({
    navigatorStyle: {
        height: 64,
        backgroundColor: '#FFFFFF',
        textAlign: 'center',
        paddingTop: 33.5,
        fontSize: 17,
        fontWeight: '600',
    },
    list: {
        //justifyContent: 'space-around',
        flexDirection: 'row',
        flexWrap: 'wrap',
        alignItems: 'flex-start',
        backgroundColor: '#FFFFFF'
    },
    row: {
        backgroundColor: '#FFFFFF',
        justifyContent: 'center',
        width: (ScreenWidth - 1) / 4,
        height: (ScreenWidth - 1) / 4,
        alignItems: 'center',
        // borderWidth: 0.5,
        // borderRadius: 5,
        // borderColor: '#E6E6E6'
    },
    sectionHeader: {
        marginLeft: 10,
        padding: 6.5,
        fontSize: 12,
        color: '#787878'
    },
    remark: {
        margin: 10,
        fontSize: 10,
        color: '#D2D2D2',
        marginBottom: 10,
        alignSelf: 'center',
    },
});

看下最終效果圖吧


完美.jpg

最最后總結(jié)一下在開發(fā)中遇到的疑難雜癥還有sectionList的重要屬性。


???.png

不知道你有沒有遇見這個問題, 看起來很簡單, 應(yīng)該是我沒有引入Text組件, 但是我確實引入了。最終發(fā)現(xiàn)這個問題竟是因為我有段代碼是這樣寫的

           <Image> source={require('../../assets/image/deadline.png')} style={styles.iconStyle} </Image>
                            <Text style={styles.userNameStyle}>賺積分,換好禮!</Text>

不知道你有沒有發(fā)現(xiàn)錯誤, 由于習(xí)慣我<Image>組件寫成<Image></Image>,Image是自封閉標簽所以

           <Image source={require('../../assets/image/deadline.png')} style={styles.iconStyle} />
                            <Text style={styles.userNameStyle}>賺積分,換好禮!</Text>

這樣問題就解決了, 但是我不清楚它為啥會報這樣的錯,反正開發(fā)中總是會出現(xiàn)一些不知所以的錯, 所以平時寫代碼的時候不斷總結(jié)起來就好啦...

SectionList 屬性

  • ItemSeparatorComponent?: ?ReactClass<any>
    行與行之間的分隔線組件。不會出現(xiàn)在第一行之前和最后一行之后。

  • ListFooterComponent?: ?ReactClass<any>
    尾部組件

  • ListHeaderComponent?: ?ReactClass<any>
    頭部組件

  • keyExtractor: (item: Item, index: number) => string
    此函數(shù)用于為給定的item生成一個不重復(fù)的key。Key的作用是使React能夠區(qū)分同類元素的不同個體,以便在刷新時能夠確定其變化的位置,減少重新渲染的開銷。若不指定此函數(shù),則默認抽取item.key作為key值。若item.key也不存在,則使用數(shù)組下標。

  • onEndReached?: ?(info: {distanceFromEnd: number}) => void
    當(dāng)所有的數(shù)據(jù)都已經(jīng)渲染過,并且列表被滾動到距離最底部不足onEndReachedThreshold個像素的距離時調(diào)用。

  • onRefresh?: ?() => void
    如果設(shè)置了此選項,則會在列表頭部添加一個標準的RefreshControl控件,以便實現(xiàn)“下拉刷新”的功能。同時你需要正確設(shè)置refreshing屬性。

  • refreshing?: ?boolean
    是否刷新嘍

  • renderItem: (info: {item: Item, index: number}) => ?React.Element<any>
    根據(jù)行數(shù)據(jù)data渲染每一行的組件。
    除data外還有第二個參數(shù)index可供使用。

  • renderSectionHeader?: ?(info: {section: SectionT}) => ?React.Element<any>
    這就是我用到的每個section的組頭

  • sections: Array<SectionT>
    你的數(shù)據(jù)源

最后再提醒一下不要忘了key key key 哦 。
歡迎大家給提意見哦, 里面還是有一些不懂與不足的地方,快用你的見解砸我吧 ??

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,622評論 6 544
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 99,716評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,746評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經(jīng)常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,991評論 1 318
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 72,706評論 6 413
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 56,036評論 1 329
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 44,029評論 3 450
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 43,203評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 49,725評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 41,451評論 3 361
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,677評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,161評論 5 365
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 44,857評論 3 351
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,266評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,606評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,407評論 3 400
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 48,643評論 2 380

推薦閱讀更多精彩內(nèi)容