在上篇文章《ReactNative——使用FlatList實現豆瓣電影列表》中我們用FlatList實現了一個豆瓣電影的列表頁。本篇我們使用SectionList將兩個電影列表頁改為一個分組的列表頁,學習一下SectionList的用法。
SectionList是用於多個分組的列表控制元件,如果你的列表不需要分組,那還是用FlatList。SectionList的資料來源屬性是sections,其作用與FlatList的data屬性一樣,都是給列表設定資料來源,但兩個屬性有所區別,需要注意。
sections是一個只讀的陣列,在渲染分組列表之前我們就需要設定好其中的資料。這個陣列中的元素可以設定為一個字典型別{key1:value1, key2:value2, …},但是data欄位和值是必須的,也就是說其資料格式通通常可以寫成下面的形式:
[
{data: {obj1, obj2, obj3, ...}, key1: value1, key2: value2, ...},
{data: {obj1, obj2, obj3, ...}, key1: value1, key2: value2, ...},
];
複製程式碼
data欄位中的資料是每個section的資料來源,是必不可少的,其它key、value可以根據實際情況自己設定。比如renderSectionHeader函式中我們想區分當前渲染的是哪一個section,可以在section的資料來源中為每條資料加入一個index:
[
{data: {obj1, obj2, obj3, ...}, index: 0},
{data: {obj1, obj2, obj3, ...}, index: 1},
];
複製程式碼
這樣在取值的時候我們可以根據index來判斷當前是哪個section並做不同的渲染。section的資料構造是非常靈活的,可以根據實際需要來構造不同的資料。
下面我們來看看怎樣改造電影列表。我們先還是構造state中的資料,如下:
constructor(props) {
super(props);
this.state = {
displayingMovies: [], // 正在上映的電影資料
incomingMovies: [], // 即將上映的電影資料
sectionData: [], // SectionList資料來源
loaded: false, // 用來控制loading檢視的顯示,當資料載入完成,loading檢視不再顯示
};
}
複製程式碼
render函式中的FlatList要改為SectionList了:
render() {
if (!this.state.loaded) {
return (
<View style={styles.loadingView}>
<ActivityIndicator animating={true} size="small"/>
<Text style={{color: `#666666`, paddingLeft: 10}}>努力載入中</Text>
</View>
)
}
return (
<SectionList
keyExtractor={this._keyExtractor}
renderSectionHeader={this._renderSectionHeader}
renderItem={this._renderItem}
sections={this.state.sectionData}
/>
)
}
複製程式碼
多了一個renderSectionHeader屬性,我們要用它來渲染一個粘性的標題元件:
_renderSectionHeader = (item) => {
let sectionObj = item.section;
let sectionIndex = sectionObj.index;
let title = (sectionIndex === 0) ? "正在上映" : "即將上映";
return (
<View style={styles.sectionHeader}>
<Text style={styles.sectionTitle}>{title}</Text>
</View>
)
};
複製程式碼
這裡就用到了index來區分section,index是我在section的資料來源陣列中設定的一個值,下面來看看介面部分的呼叫邏輯。我們有兩個介面需要呼叫,這兩個介面都是非同步呼叫的,我們需要得到兩個列表資料displayingMovies和incomingMovies,並對這兩個陣列進行處理,構造sectionData資料。
這裡我修改了一下介面呼叫的邏輯,分三步:
- 先獲取正在上映的電影資料,得到displayingMovies;
/**
* 先載入正在上映的電影列表,如果載入成功,接著獲取即將上映的電影資料
*/
loadDisplayingMovies() {
let that = this;
fetch(queryMovies(`北京`, 0, 20)).then((response) => response.json()).then((json) => {
console.log(json);
let movies = [];
for (let idx in json.subjects) {
let movieItem = json.subjects[idx];
let directors = ""; // 導演
for (let index in movieItem.directors) {
// 得到每一條電影的資料
let director = movieItem.directors[index];
// 將多個導演的名字用空格分隔開顯示
if (directors === "") {
directors = directors + director.name
} else {
directors = directors + " " + director.name
}
}
movieItem["directorNames"] = directors;
// 拼裝主演的演員名字,多個名字用空格分隔顯示
let actors = "";
for (let index in movieItem.casts) {
let actor = movieItem.casts[index];
if (actors === "") {
actors = actors + actor.name
} else {
actors = actors + " " + actor.name
}
}
movieItem["actorNames"] = actors;
movies.push(movieItem)
}
// 注意此處setState的寫法,表示setState完成後再呼叫loadComingMovies()方法,此時state中的資料已經完成更新了。
that.setState(
{
displayingMovies: movies,
},
() => {
// 載入完正在上映的電影后再接著載入即將上映的電影資料
that.loadComingMovies();
}
)
}).catch((e) => {
console.log("載入失敗");
that.setState({
loaded: true
})
}).done();
}
複製程式碼
- 獲取成功後接著呼叫介面獲取即將上映的電影資料,得到incomingMovies;
- 兩個資料都拿到後再更新sectionData,介面重新整理後資料就顯示出來了。
/**
* 載入即將上映的電影列表,並更新sectionData重新整理列表
*/
loadComingMovies() {
let that = this;
fetch(comingMovies(`北京`, 0, 20)).then((response) => response.json()).then((json) => {
console.log(json);
if (json == null) {
that.setState({
loaded: true,
});
return
}
let movies = [];
for (let idx in json.subjects) {
let movieItem = json.subjects[idx];
let directors = "";
for (let index in movieItem.directors) {
let director = movieItem.directors[index];
if (directors === "") {
directors = directors + director.name
} else {
directors = directors + " " + director.name
}
}
movieItem["directorNames"] = directors;
let actors = "";
for (let index in movieItem.casts) {
let actor = movieItem.casts[index];
if (actors === "") {
actors = actors + actor.name
} else {
actors = actors + " " + actor.name
}
}
movieItem["actorNames"] = actors;
movies.push(movieItem)
}
// 兩個電影資料都載入完成後需要更新sectionData,將資料在介面上顯示出來
let sectionList = [
{data: that.state.displayingMovies, index: 0},
{data: movies, index: 1},
];
that.setState({
loaded: true,
incomingMovies: movies,
sectionData: sectionList
});
}).catch((error) => {
console.log("載入失敗");
that.setState({
loaded: true
})
}).done();
}
複製程式碼
這樣我們就完成了資料的載入,介面也成功重新整理出來,以下是改造之後的效果圖:
SectionList的使用關鍵就是弄清楚sections這個屬性,怎樣構造資料來源資料,在使用時取值還要注意,多除錯一步步弄清楚每個section,每個item中資料結構是怎樣的,避免出錯。
Demo地址: github.com/mrarronz/re…