在上篇文章《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…