`
jsntghf
  • 浏览: 2476009 次
  • 性别: Icon_minigender_1
  • 来自: 苏州
社区版块
存档分类
最新评论

React Native之构建一个简单的列表页

    博客分类:
  • iOS
阅读更多

本文中我们将创建一个简单的电影应用,这个应用将从Rotten Tomatoes网站抓取目前正在上映的最新的25部电影,并将它们展示在一个ListView中。

 

一、伪造数据

 

在我们开始编写代码从Rotten Tomatoes网站抓取数据之前,我们先来伪造一些数据,以便我们可以马上体验一下React Native。我们一般会在JS文件的顶部声明常量,并在后面使用。在index.ios.js中添加以下代码:

var MOCKED_MOVIES_DATA = [
  {title: 'Title', year: '2015', posters: {thumbnail: 'http://i.imgur.com/UePbdph.jpg'}},
];

 

二、渲染一部电影

 

我们会渲染电影的标题、年份以及海报缩略图。由于缩略图在React Native中是一个Image组件,所以我们需要将Image组件加到React的依赖项中。

var {
  AppRegistry,
  Image,
  StyleSheet,
  Text,
  View,
} = React;

 

现在我们来修改render函数,以便我们可以渲染上面的模拟数据。

 

render: function() {
    var movie = MOCKED_MOVIES_DATA[0];
    return (
      <View style={styles.container}>
        <Text>{movie.title}</Text>
        <Text>{movie.year}</Text>
	    <Image
        	source={{uri: movie.posters.thumbnail}}
        	style={styles.thumbnail}
	 	/>
      </View>
    );
}

 

同时我们还需要修改styles,用来将样式应用到相应的组件上。

 

var styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
});

 

按下cmd+R,此时的电影标题、年份以及海报缩略图已经渲染出来了。



 

三、修改样式,将文字放在图片的右侧

 

接下来,我们把文字放在图片的右侧,同时让标题字体大一些并居中显示。

 

修改render函数,添加另一个View,这是为了让我们的组件在最外层的组件中垂直居中显示。

 

render: function() {
    var movie = MOCKED_MOVIES_DATA[0];
    return (
      <View style={styles.container}>
        <Image
    	    source={{uri: movie.posters.thumbnail}}
    	    style={styles.thumbnail}
 	    />
		<View style={styles.rightContainer}>
        	<Text style={styles.title}>{movie.title}</Text>
        	<Text style={styles.year}>{movie.year}</Text>
        </View>
      </View>
    );
}

 

同时我们还需要修改styles,用来将样式应用到相应的组件上。

 

var styles = StyleSheet.create({
  container: {
    flex: 1,
	flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
  rightContainer: {
    flex: 1,
  },
  title: {
    fontSize: 20,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    textAlign: 'center',
  },
});

 

按下cmd+R来查看更新之后的视图。



 

四、抓取真实数据

 

将下面的常量放在文件的顶部,用来创建一个请求数据使用的REQUEST_URL:

 

var API_KEY = '7waqfqbprs7pajbz28mqf6vz';
var API_URL = 'http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json';
var PAGE_SIZE = 25;
var PARAMS = '?apikey=' + API_KEY + '&page_limit=' + PAGE_SIZE;
var REQUEST_URL = API_URL + PARAMS;

 

为我们的应用添加初始状态,这样我们可以通过检查this.state.movies === null来确定电影数据有没有被加载。当电影数据返回时,我们可以通过this.setState({movies: moviesData})来设置数据。

 

将下面的代码添加到render函数之前:

 

getInitialState: function() {
    return {
      movies: null,
    };
},

 

我们想要在组件完成加载后发送请求,componentDidMount是React组件中的一个函数,它只会在组件加载完成后被调用一次。

 

componentDidMount: function() {
    this.fetchData();
},

 

接下来添加组件中会用到的fetchData函数,这个函数将负责处理数据的抓取。你需要做的仅仅是在promise完成解析后调用this.setState({movies: data})。因为setState会触发重新渲染,而此时render函数会注意到this.state.movies不再是null。

 

注意一定要在promise链的最后调用done(),否则错误信息可能会被忽略。

 

fetchData: function() {
    fetch(REQUEST_URL)
      .then((response) => response.json())
      .then((responseData) => {
        this.setState({
          movies: responseData.movies,
        });
      })
      .done();
},

 

现在修改render函数,如果电影数据还没有返回的话,就渲染一个loading视图,否则将渲染第一部电影。

 

render: function() {
    if (!this.state.movies) {
      return this.renderLoadingView();
    }

    var movie = this.state.movies[0];
    return this.renderMovie(movie);
},

renderLoadingView: function() {
    return (
      <View style={styles.container}>
        <Text>
          Loading movies...
        </Text>
      </View>
    );
},

renderMovie: function(movie) {
    return (
      <View style={styles.container}>
        <Image
          source={{uri: movie.posters.thumbnail}}
          style={styles.thumbnail}
        />
        <View style={styles.rightContainer}>
          <Text style={styles.title}>{movie.title}</Text>
          <Text style={styles.year}>{movie.year}</Text>
        </View>
      </View>
    );
},

 

按下cmd+R,首先会看到loading页面




电影数据返回后,就会渲染第一部从Rotten Tomatoes获取过来的电影。



 

五、显示完整的电影列表

 

现在我们来修改应用,将所有的数据渲染在一个ListView组件中,而不是只渲染一部电影。

 

这里使用ListView是因为ListView会自动渲染视线之内的视图,而那些在屏幕之外的视图会被暂时移除。

 

这里既然用到了ListView,我们就需要将ListView组件加到React的依赖项中。

 

var {
  AppRegistry,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
} = React;

 

接下来修改render函数,一旦数据返回就可以在ListView里面渲染数据:

 

render: function() {
	if (!this.state.loaded) {
	      return this.renderLoadingView();
	}

	return (
	  <ListView
	    dataSource={this.state.dataSource}
	    renderRow={this.renderMovie}
	    style={styles.listView}
	  />
	);
},

 

DataSource是ListView的一个接口,作用是决定哪些行会改变。

 

接下来,我们需要在getInitialState的返回对象上添加一个空的dataSource,我们不能再使用this.state.movies防止数据被存储两次。我们可以使用this.state.loaded来判断数据抓取是否结束。

 

getInitialState: function() {
    return {
      dataSource: new ListView.DataSource({
        rowHasChanged: (row1, row2) => row1 !== row2,
      }),
      loaded: false,
    };
},

 

我们还需要修改fetchData来更新state:

 

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

 

最后,我们在styles中为ListView组件添加样式: 

 

listView: {
    paddingTop: 20,
    backgroundColor: '#F5FCFF',
},

 

下面是最终的效果图:



 

六、本文最终完整的源码

 

'use strict';

var React = require('react-native');

var API_KEY = '7waqfqbprs7pajbz28mqf6vz';
var API_URL = 'http://api.rottentomatoes.com/api/public/v1.0/lists/movies/in_theaters.json';
var PAGE_SIZE = 25;
var PARAMS = '?apikey=' + API_KEY + '&page_limit=' + PAGE_SIZE;
var REQUEST_URL = API_URL + PARAMS;

var {
  AppRegistry,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
} = React;

var Movie = React.createClass({
	getInitialState: function() {
	    return {
	      dataSource: new ListView.DataSource({
	        rowHasChanged: (row1, row2) => row1 !== row2,
	      }),
	      loaded: false,
	    };
	},
	componentDidMount: function() {
	    this.fetchData();
	},
	fetchData: function() {
	    fetch(REQUEST_URL)
	      .then((response) => response.json())
	      .then((responseData) => {
	        this.setState({
	          dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
	          loaded: true,
	        });
	      })
	      .done();
	},
	render: function() {
		if (!this.state.loaded) {
		      return this.renderLoadingView();
		}

		return (
		  <ListView
		    dataSource={this.state.dataSource}
		    renderRow={this.renderMovie}
		    style={styles.listView}
		  />
		);
	},

	renderLoadingView: function() {
	    return (
	      <View style={styles.container}>
	        <Text>
	          Loading movies...
	        </Text>
	      </View>
	    );
	},

	renderMovie: function(movie) {
	    return (
	      <View style={styles.container}>
	        <Image
	          source={{uri: movie.posters.thumbnail}}
	          style={styles.thumbnail}
	        />
	        <View style={styles.rightContainer}>
	          <Text style={styles.title}>{movie.title}</Text>
	          <Text style={styles.year}>{movie.year}</Text>
	        </View>
	      </View>
	    );
	},
});

var styles = StyleSheet.create({
  container: {
    flex: 1,
	flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
  rightContainer: {
    flex: 1,
  },
  title: {
    fontSize: 20,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    textAlign: 'center',
  },
  listView: {
      paddingTop: 20,
      backgroundColor: '#F5FCFF',
  },
});

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

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics