React-Native学习笔记(二)--ListView

写在前面的话

RN初学者,写这些文章一是帮自己回顾知识,二是帮助下像我这样没有前端基础却对RN这块感兴趣的同学。大致会按照宁皓网上的demo来写。有兴趣的同学可以直接去网站里看视频(收费的)

ListView

写过原生的同学都应该了解ListView是多么的重要,在RN里facebook为我们封装好了ListView组件,我们先看下官方文档里的说明。这里官方给我们提供了一个最简单的例子

constructor(props) {
  super(props);
  var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
  this.state = {
    dataSource: ds.cloneWithRows(['row 1', 'row 2']),
  };
}
render() {
  return (
    <ListView
      dataSource={this.state.dataSource}
      renderRow={(rowData) => <Text>{rowData}</Text>}
    />
  );
}

在ListView中有两个属性dataSourcerenderRow,其中dataSource就是数据源,类似原生中adapter中传入的list,renderRow就是每一行item的样式,类似adapter中给item设置的layoutconstructor方法就是构造方法,会在程序被调用时运行。rowHasChanged表示只更新有数据变化的item

this.state = {
    dataSource: ds.cloneWithRows(['row 1', 'row 2']),
  };

state就是表示组件的一个状态,这里设置了一个状态dataSource,他的值就是ds.cloneWithRows(['row 1', 'row 2']),然后在ListView组件中使用了这个状态dataSource={this.state.dataSource}。这样就是将数据源交给了ListView。(如果不明白state的可以先看一下React入门熟悉下React的语法)

实现一个电影ListView

首先创建一个MovieList项目react-native init MovieList,等待一会大约10分钟(是不是可以来两局皇室战争呢(手动斜眼笑))。这里强烈推荐修改hosts来翻墙,会快很多。


好了,项目创建好了后,用atom打开(配置atom插件,我用着atom没感觉到卡,一定要装language-babel这个插件啊。要是不喜欢,也可以用sublime),我们先直接修改index.android.js。先按照官方给出的例子来写点数据显示出来。

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  ListView,
  View
} from 'react-native';


let data = ['apple','pear','banana','orange','apple','pear','banana',
'orange','apple','pear','banana','orange','apple','pear','banana','orange',
'apple','pear','banana','orange','apple','pear','banana','orange'];

class MovieList extends Component {
    constructor(){
        super();
        this.dataSource = new ListView.DataSource({
            rowHasChanged:(row1,row2) => row1 !== row2
        })
    };

    render() {
        return (
          <View style={styles.container}>
            <ListView
                dataSource={this.dataSource.cloneWithRows(data)}
                renderRow={(rowData) =>
                    <View style={{flex:1,margin:10}}>
                        <Text style={{fontSize:25}}>{rowData}</Text>
                    </View>}
                />
          </View>
        );
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
});

AppRegistry.registerComponent('MovieList', () => MovieList);

效果图:


水果list效果图

如果有的同学也用的genymotion,也用的android6.0,那么可能会出现这样的一个错误:


错误1

这种情况只要把模拟器的wifi连接就可以了。

这里我们用到了行内样式

<View style={styles.container}>
            <ListView
                dataSource={this.dataSource.cloneWithRows(data)}
                renderRow={(rowData) =>
                    <View style={{flex:1,margin:10}}>
                        <Text style={{fontSize:25}}>{rowData}</Text>
                    </View>}
                />
          </View>

简单的一些样式,就可以采用行内样式的写法来处理。不明白箭头函数的可以看这里

上面就是把一些简单的数据用list的方式显示出来了,现在我们就来显示一些真实的数据,这里我们使用豆瓣的api来拿数据来显示电影的名称。网络请求使用fetch

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  ListView,
  View
} from 'react-native';

const REQUEST_URL = 'https://api.douban.com/v2/movie/top250';

class MovieList extends Component {
    constructor(){
        super();
        this.state = {
            movies:new ListView.DataSource({
                rowHasChanged:(row1,row2) => row1 !== row2
            })
        }
        this.fetchData();
    };

    fetchData(){
        fetch(REQUEST_URL)
            .then(response => response.json())
            .then(responseData => {
                console.log(responseData);
                this.setState({
                    movies:this.state.movies.cloneWithRows(responseData.subjects)
                })
            })
            .done();
    };

    render() {
        return (
          <View style={styles.container}>
            <ListView
                dataSource={this.state.movies}
                renderRow={(movie) =>
                    <View style={{flex:1,margin:10}}>
                        <Text style={{fontSize:25}}>{movie.title}</Text>
                    </View>}
                />
          </View>
        );
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
});

AppRegistry.registerComponent('MovieList', () => MovieList);

效果图:


豆瓣TOP250

请求数据的方法fetchData的理解可以结合log来理解(不会调试的看这里

现在我们能够显示出电影的名字,但为免也太单调了,我们接着把电影的海报,别名,评分也显示出来。首先我们要调整一下listview的item的样式,其中要使用的另一个组件image,同时也将样式的写法统一。在使用到组件的时候需要在顶部的import里将组件包含进来。

注意在使用image组件的时候一定要给图片设置宽高,图片才会显示。

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  ListView,
  Image,
  View
} from 'react-native';

const REQUEST_URL = 'https://api.douban.com/v2/movie/top250';

class MovieList extends Component {
    constructor(){
        super();
        this.state = {
            movies:new ListView.DataSource({
                rowHasChanged:(row1,row2) => row1 !== row2
            })
        }
        this.fetchData();
    };

    fetchData(){
        fetch(REQUEST_URL)
            .then(response => response.json())
            .then(responseData => {
                console.log(responseData);
                this.setState({
                    movies:this.state.movies.cloneWithRows(responseData.subjects)
                })
            })
            .done();
    };

    renderMovieList(movie){
      return(
          <View style={styles.item}>
            <View style={styles.itemImage}>
              <Image
                style={styles.image}
                source={{uri:movie.images.large}} />
            </View>
            <View style={styles.itemContent}>
              <Text style={styles.itemHeader}>
                {movie.title}
              </Text>
              <Text style={styles.itemMeta}>
                {movie.original_title} ({movie.year})
              </Text>
              <Text style={styles.redText}>
                {movie.rating.average}
              </Text>
            </View>
          </View>
      );
    }

    render() {
        return (
          <View style={styles.container}>
            <ListView
                dataSource={this.state.movies}
                renderRow={
                    this.renderMovieList.bind(this)}
                />
          </View>
        );
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
  item:{
    flexDirection:'row',
    borderBottomWidth:1,
    borderColor:'rgba(100,53,201,0.1)',
    paddingBottom:6,
    paddingTop:6,
    flex:1,
  },
  itemText:{
    fontSize:16,
    fontFamily:'Helvetica Neue',
    fontWeight:'400',
    color:'rgba(0,0,0,0.8)',
    lineHeight:26,
  },
  image:{
    height:138,
    width:99,
    margin:6,
  },
  itemHeader:{
    fontSize:18,
    fontFamily:'Helvetica Neue',
    fontWeight:'300',
    color:'#6435c9',
    marginBottom:6,
  },
  itemContent:{
    flex:1,
    marginLeft:13,
    marginTop:6,
  },
  itemMeta:{
    fontSize:16,
    color:'rgba(0,0,0,0.6)',
    marginBottom:6,
  },
  redText:{
    color:'#db2828',
    fontSize:15,
  },
});

AppRegistry.registerComponent('MovieList', () => MovieList);

效果图:


豆瓣电影TOP250

这里可以看到图片,文字都正常的显示出来了,但在我们第一次加载的时候可以看到有很长时间的白屏时间,这就是我们的fetchData方法在请求数据,正常情况下这里我们应该给用户一个正在加载的提示,我们将使用ActivityIndicator组件,他的显示效果就是在不停的转圈圈(ios中是小菊花,嘿嘿嘿)。配合这个组件,在请求数据的时候显示转圈圈(小菊花),数据请求完成后组件消失,显示正常的数据。这里就需要在state中加一个loaded变量,在请求数据的时候为false,请求成功后为true。(其实也就是设一个flag)

import React, { Component } from 'react';
import {
  AppRegistry,
  StyleSheet,
  Text,
  ListView,
  Image,
  ActivityIndicator,
  View
} from 'react-native';

const REQUEST_URL = 'https://api.douban.com/v2/movie/top250';

class MovieList extends Component {
    constructor(){
        super();
        this.state = {
            movies:new ListView.DataSource({
                rowHasChanged:(row1,row2) => row1 !== row2
            }),
            loaded:false
        }
        this.fetchData();
    };

    fetchData(){
        fetch(REQUEST_URL)
            .then(response => response.json())
            .then(responseData => {
                console.log(responseData);
                this.setState({
                    movies:this.state.movies.cloneWithRows(responseData.subjects),
                    loaded:true
                })
            })
            .done();
    };

    renderMovieList(movie){
      return(
          <View style={styles.item}>
            <View style={styles.itemImage}>
              <Image
                style={styles.image}
                source={{uri:movie.images.large}} />
            </View>
            <View style={styles.itemContent}>
              <Text style={styles.itemHeader}>
                {movie.title}
              </Text>
              <Text style={styles.itemMeta}>
                {movie.original_title} ({movie.year})
              </Text>
              <Text style={styles.redText}>
                {movie.rating.average}
              </Text>
            </View>
          </View>
      );
    }

    render() {
        if (!this.state.loaded) {
            return(
                <View style={styles.container}>
                  <View style={styles.loading}>
                    <ActivityIndicator
                      size='large'
                      color='#eabb33'/>
                  </View>
                </View>
            )
        }
        return (
          <View style={styles.container}>
            <ListView
                dataSource={this.state.movies}
                renderRow={
                    this.renderMovieList.bind(this)}
                />
          </View>
        );
    }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#F5FCFF',
  },
  item:{
    flexDirection:'row',
    borderBottomWidth:1,
    borderColor:'rgba(100,53,201,0.1)',
    paddingBottom:6,
    paddingTop:6,
    flex:1,
  },
  itemText:{
    fontSize:16,
    fontFamily:'Helvetica Neue',
    fontWeight:'400',
    color:'rgba(0,0,0,0.8)',
    lineHeight:26,
  },
  image:{
    height:138,
    width:99,
    margin:6,
  },
  itemHeader:{
    fontSize:18,
    fontFamily:'Helvetica Neue',
    fontWeight:'300',
    color:'#6435c9',
    marginBottom:6,
  },
  itemContent:{
    flex:1,
    marginLeft:13,
    marginTop:6,
  },
  itemMeta:{
    fontSize:16,
    color:'rgba(0,0,0,0.6)',
    marginBottom:6,
  },
  redText:{
    color:'#db2828',
    fontSize:15,
  },
  loading:{
    flex:1,
    justifyContent:'center',
    alignItems:'center',
  },
});

AppRegistry.registerComponent('MovieList', () => MovieList);

这里其实就是在render方法里加了一个判断,效果图:

豆瓣电影TOP250转圈圈

这里我们一个简单的MovieList就写好了。下面放一张IOS的效果图,代码可以完全不变直接复制到index.ios.js中。(这就是RN的优势,代码复用率基本在80%以上,而且现在我们采用的是复制的方法,还比较麻烦,下个笔记将会把这个页面提取出来,这样更方便。同时推荐开发RN还是弄个OS X系统吧,白苹果黑苹果的都可以,我的就是黑苹果,确实方便很多)

豆瓣电影TOP250小菊花

写在最后的话

这段时间基本会一周写一篇关于RN的文章,直到我江郎才尽写不出来为止。会尽量把我会的东西写出来,写明白。我也是在学习,如果能把东西写明白了那么应该也是有一定的理解了。如果有问题可以给我留言,我们一起讨论。

如果想看我以前关于android的文章,戳这里(其实也没有多少东西(╯‵□′)╯︵┻━┻)。

学习路漫漫,吾将上下而求索。

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

推荐阅读更多精彩内容