React Native ListView section踩坑记

第一种情况

需求:在不同的section里面展示从同一个数据源获取的不同内容。获取dataSource后,将其转换成对象。 我的项目要求是判断某条数据的截止时间是否小于当前时间,如果小于则属于过期活动,放入过期活动列表,如果大于当前时间,则放入当前活动列表。如下:

GIF5.gif

关于ListView的基础知识就不提了,dataSource里面的数据格式最常用的就是下面两种:

  1. { sectionID_1: [ rowData1, rowData2, ... ], ... }
  2. [ [ rowData1, rowData2, ... ], ... ]

第二种数据格式就是最简单的纯数组格式,使用cloneWithRows()方法
今天我的目标是用第一种数据格式。我将我请求的数据格式写在本地json文件里
test.json:

{
  "data": [
    {
      "id": 1,
      "title": "活动标题2",
      "column_url": "http://upload.s.vidahouse.com/FgMw2dLfy2Ml2mUyodXI8_pxJ7xZ?imageMogr2/crop/!1822.7848101265824x729.1139240506329a48.607594936708864a174.9873417721519/thumbnail/!750x300r",
      "start_time": 1495598400000,
      "end_time": 1496203200000,
      "created_at": 0
    },
    {
      "id": 3,
      "title": "活动标题3",
    
      "column_url": "http://upload.s.vidahouse.com/Fpm7ElJmma6nHU_isG7RgIUcR-Xe?imageMogr2/crop/!1367.0886075949368x546.8354430379746a36.45569620253164a174.9873417721519/thumbnail/!750x300r",
      "start_time": 1494907200000,
      "end_time": 1495512000000,
      "created_at": 0
    },
    {
      "id": 5,
      "title": "活动标题4",
      "column_url": "http://upload.s.vidahouse.com/FgMw2dLfy2Ml2mUyodXI8_pxJ7xZ?imageMogr2/crop/!1822.7848101265824x729.1139240506329a48.607594936708864a174.9873417721519/thumbnail/!750x300r",
      "start_time": 1494907200000,
      "end_time": 1496894400000,
      "created_at": 0
    },
     {
      "id": 7,
      "title": "活动标题4",
      "column_url": "http://upload.s.vidahouse.com/FgMw2dLfy2Ml2mUyodXI8_pxJ7xZ?imageMogr2/crop/!1822.7848101265824x729.1139240506329a48.607594936708864a174.9873417721519/thumbnail/!750x300r",
      "start_time": 1495598400000,
      "end_time": 1695598400000,
      "created_at": 0
    },
  ]
}

如果你们运行了React Native项目,在项目根目录下新建一个test.json,将以上代码拷贝进去,访问http://localhost:8081/test.json 就能访问成功。

ListView定义dataSource:

constructor(props) {
    super(props);
    let ds = new ListView.DataSource({ 
      rowHasChanged: (r1,r2) => r1 !== r2,
      sectionHeaderHasChanged: (s1, s2) => s1 !== s2
     });
    this.state = {
      dataSource: ds,
    };
    this.allActivityData = [];
    this.renderRow = this.renderRow.bind(this);
  }

注意一下如果有section,则还需要加sectionHeaderHasChanged: (s1, s2) => s1 !== s2为数据源赋值。
componentDidMount()生命周期里面请求数据:

componentDidMount() {
   fetch('http://localhost:8081/test.json')
      .then((response) => response.json())
      .then((responseJson) => {
        console.log(responseJson.data)
        this.allActivityData = responseJson.data;
        this.setState({
          dataSource: this.state.dataSource.cloneWithRowsAndSections(this._genRow())
        })
      })
      .catch((error) => {
        console.log(error)
      })
  }

首先,我先将获取的数据传给 allActivityData这个变量,这个变量在_genRow()函数中要用到,注意一下这里我传递的参数是this._genRow(),这个函数做了什么呢?

_genRow() {
    let currentData = [];
    let expiredData = [];
    let data = this.allActivityData;
    let results = {};
    for (let i = 0; i < data.length; i++) {
      if(data[i].end_time > currentTime) {
        currentData.push(data[i]);
      } else {
        expiredData.push(data[i]);
      }
    }
    results = {
      "当前活动": currentData,
      "往期活动": expiredData,
    };
    return results
  }

前面我说了,我获取数据是从一个接口拿,拿到的数据有一个字段end_time,存放的是活动结束时间,格式是时间戳,如果结束时间在当前时间之前,那么活动过期。我定义的两个变量 currentDataexpiredData是分别用于存放过期活动数据和当前活动数据的。格式依然是数组。
重点来了,再看一次有section时,dataSource的数据格式:{ sectionID_1: [ rowData1, rowData2, ... ], ... },在这里,我要做的就是给 currentDataexpiredData加一个头部信息,将返回的数据格式变为对象:

 results = {
      "当前活动": currentData,
      "往期活动": expiredData,
    };
    return results

剩下的,关于如何渲染这些数据,ListView已经帮我们做好了。

<ListView
            enableEmptySections = {true} 
            dataSource = {this.state.dataSource}
            renderRow  = {this.renderRow}
            renderSectionHeader = {(sectionData, sectionID) => this._renderHeader(sectionData, sectionID)}
/>

renderRow就是用来渲染列表,renderSectionHeader是用来渲染头部信息。

renderRow(rowData, sectionID, rowID) {
    return (
      <View style = {{height: 270 / 667 * deviceHeight, width: deviceWidth,}}>
        <Image source = {{uri:rowData.column_url}} style = {{width: deviceWidth, height: 220 / 667 * deviceHeight}}/>
        <View 
          style = {{
            alignItems: 'flex-end',
            backgroundColor: '#fff', 
            borderBottomWidth: 1, 
            borderColor: '#eee', 
            justifyContent: 'center',
            height: 50 / 667 * deviceHeight,
            paddingHorizontal: 10,
            width: deviceWidth,
          }}
        >
        </View>
      </View>
    )
_renderHeader(sectionData, sectionID) {
    return(
      <View style = {{height: 50 / 667 * deviceHeight, paddingLeft: 15, flexDirection: 'row', alignItems: 'center'}}>
        <View style = {{backgroundColor: '#ccc',height: 20 / 667 * deviceHeight,width: 2,}}></View>
        <Text style = {{color: '#000', fontSize: 16,fontWeight: 'bold', paddingLeft: 5,}}>{sectionID}</Text>
      </View>
    )

  }

注意在_renderHeader()参数里面,sectionData就是renderRowrowDatasectionID才是我们添加的‘往期活动’和‘当前活动’。

第二种情况

需求:头部有一条内容,下面是一个ListView列表,头部的内容可能是一个写死的展示信息而不是真正的数据,我一开始的做法是在最外层加一个ScrollView,里面又嵌套ListView,他们的移动方向都是一样的,当用户手势识别,设备并不知道用户滑动了哪一个组件,这是有问题的。最好的方式是将第一条展示信息加到renderRow()里面,也就是整个页面只有一个ListView。如下:

2017-06-21_102458.jpg

上方是一个用户信息展示,下方是一个ListView展示的用户作品,整个页面是可滚动的,我的目的是将用户信息展示放入到ListViewrenderRow()里面,实现整个页面可滚动,并且没有嵌套滚动。
这里的做法和上一种情况有一些区别,就是第一条内容不是从数据库获取的数据,而只是一个普通的展示view

constructor(props) {
    super(props);
    let ds = new ListView.DataSource({
      rowHasChanged: (r1,r2) => r1 !== r2,
      sectionHeaderHasChanged: (s1, s2) => s1 !== s2
    })
    this.state = {
      dataSource: ds,
    }
    this.data = []
  }

和上面一样,我要采用的数据格式仍然是一个对象,所以需要有sectionHeaderHasChanged方法,dataSourceListView的数据源。
获取数据:

getActivityWorksCallback(status, response) {
    if(status) {
      this.data = [...this.data, ...response.data]
      this.setState({
        dataSource:         this.state.dataSource.cloneWithRowsAndSections(this._genRow())
      })
    }
  }

this.data是传递给this._genRow()处理的数据。

_genRow() {
    let obj = {
      header: [1],
      body: this.data
    }
    return obj
  }

我将获取的数据放在body中,由于头部的 view里面并不需要数据,所以我这里随意给了一个值给header,我需要的是sectionID,根据sectionID的值去判断渲染头部还是获取的数据。

<ListView
          style = {{marginBottom: 15 / 667 * deviceHeight}}
          dataSource={this.state.dataSource}
          renderRow = {this._renderRow}
          onEndReached = {()=> this._loadMore()}
          enableEmptySections = {true}
/>

我并没有将头部放在renderHeader()里面,而是直接放入了renderRow()里面,两种方式都是可以的,只不过要注意一下如果放在renderHeader()里面,iOS是会将header粘在顶部的,记得将这个属性值改成false
renderRow()里面:

_renderRow(rowData, sectionID, rowID) {
    if(sectionID == 'header') {
      return(
        {/*头部的视图*/}
      )
    }
    return(
      {/*真正获取的数据视图*/}
    )
GIF2.gif

总结

其实对于我而言之前最大的难点在于我不知道怎么修改我拿到数据的格式,怎么添加字段进去,到目前为止,我从服务器获取的数据都是在一个大的数组里面,而很多页面的需求是顶部有一个静态展示的视图,下方是一个ListView,最好的方式是将第一条展示信息加到dataSourcesectionHeader中,如果你懂得了怎么随心所欲的去修改数据的格式,那么学会用section也就不是难事了。
一起加油吧!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,884评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,347评论 3 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 157,435评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,509评论 1 284
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,611评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,837评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,987评论 3 408
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,730评论 0 267
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,194评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,525评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,664评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,334评论 4 330
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,944评论 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,764评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,997评论 1 266
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,389评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,554评论 2 349

推荐阅读更多精彩内容

  • 前言 学习本系列内容需要具备一定 HTML 开发基础,没有基础的朋友可以先转至 HTML快速入门(一) 学习 本人...
    珍此良辰阅读 13,329评论 11 24
  • 不管是大人还是孩子,都会面临自控能力不强的问题,对于孩子更是如此,基本是具有普遍现象的。如何能够有效提高孩子的自控...
    松球42阅读 322评论 0 2
  • 复习了一些iOS里大神写的KVO官方文档翻译和其他的博客,记录下来一些方便自己以后回来看。 <NSKeyValue...
    37b32340bcbc阅读 672评论 0 7
  • 近来,在工作和生活方面感触蛮深的。 太多的事情总是事与愿违。 上半年过完后,每一天都过得,嗯,挺“分崩离析”的。 ...
    O佳音O阅读 264评论 0 0
  • 舞动生命
    lygly9阅读 201评论 0 0