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