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

作者: 一点都不机智的江先生 | 来源:发表于2016-08-28 18:35 被阅读418次

    写在前面的话

    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的文章,戳这里(其实也没有多少东西(╯‵□′)╯︵┻━┻)。

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

    相关文章

      网友评论

      本文标题:React-Native学习笔记(二)--ListView

      本文链接:https://www.haomeiwen.com/subject/ivatettx.html