React Native——使用SectionList改造电影列表

在上篇文章《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数据。

这里我修改了一下接口调用的逻辑,分三步:

  1. 先获取正在上映的电影数据,得到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();
}
复制代码
  1. 获取成功后接着调用接口获取即将上映的电影数据,得到incomingMovies;
  2. 两个数据都拿到后再更新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…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值