React Native快速开发iOS和Android应用教程

Viewed 0

序言

这篇教程旨在让你使用 React Native 快速开发 iOS 和 Android 应用。如果你想知道什么是 React Native 以及为什么 Facebook 构建了它,可以阅读相关文章来了解背景。我们假设你已有使用 React 开发应用的经验;如果没有,建议先学习 React 基础知识。

安装

React Native 需要一些基本的安装依赖,具体步骤可参考官方文档。安装完成后,通过以下两条命令来初始化一个新项目:

  1. 运行 npm install -g react-native-cli 来全局安装 React Native 命令行工具。
  2. 运行 react-native init AwesomeProject 来创建一个新项目,这会生成 iOS 和 Android 工程文件。

开发

在 iOS 端,你可以在 Xcode 中打开生成的项目文件,然后使用 ⌘+R 构建和运行,同时开启 Node 服务器支持代码实时渲染。在 Android 端,运行 react-native run-android 来安装应用到模拟器,并开启 Node 服务器;通过设备震动菜单(模拟器中按 F2 或 Page Up)选择 "Reload JS" 来查看更改。

本教程将引导你开发一个简单的电影应用,用于获取并显示 25 部电影信息。

Hello World

react-native init 生成的默认应用是一个简单的 "Hello World" 程序。你可以编辑 index.ios.js(iOS)或 index.android.js(Android)来修改应用,然后通过 ⌘+R 或 "Reload JS" 查看变化。

伪造数据

在获取真实数据之前,我们可以先用模拟数据开始。在 index.ios.jsindex.android.js 文件顶部定义常量:

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

渲染一部电影

我们需要渲染电影的标题、年份和缩略图。首先,在 React 引入中增加 Image 组件:

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

然后修改渲染函数来显示模拟数据:

  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}} />
      </View>
    );
  }

此时运行应用,你会发现图片未显示,因为缺少宽度和高度样式。接下来定义样式并应用到图片:

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

将样式添加到 Image 组件:<Image source={{uri: movie.posters.thumbnail}} style={styles.thumbnail} />,重新加载后图片即可正常渲染。

增加一些样式

现在让布局更美观:将文字放在图片右侧,并调整标题和年份的样式。修改渲染函数如下:

      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>
      );

更新样式对象,使用 FlexBox 进行水平布局:

  container: {
    flex: 1,
    flexDirection: 'row',
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#F5FCFF',
  },
  rightContainer: {
    flex: 1,
  },
  title: {
    fontSize: 20,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    textAlign: 'center',
  },

重新加载应用,你会看到更新后的视图,文字和图片布局更加合理。

获取真实数据

现在从远程 API 获取真实数据。在文件顶部定义请求 URL:

var REQUEST_URL = 'https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json';

为组件添加初始状态,用于跟踪数据加载情况:

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

在组件加载完成后发起数据请求:

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

实现 fetchData 方法处理数据获取:

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

修改渲染函数,在数据未加载时显示加载视图,否则渲染第一部电影:

  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>
    );
  },

重新加载应用,你会先看到 "Loading movies...",然后渲染出获取到的第一部电影数据。

ListView

为了渲染所有电影数据,我们改用 ListView 组件。首先在引入中增加 ListView:

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

修改渲染函数,使用 ListView 来显示数据列表:

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

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

更新初始状态,包括一个空的 dataSource 和加载状态:

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

调整 fetchData 方法,将获取的数据设置到 dataSource:

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

为 ListView 添加样式:

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

现在重新运行应用,你将看到一个完整的电影列表。这只是一个基础示例,实际应用中可以添加导航栏、搜索框等功能来完善。

最终源代码

以下是完整的代码示例,供参考:

/**
 * Sample React Native App
 * https://github.com/facebook/react-native
 */
'use strict';

var React = require('react-native');
var {
  AppRegistry,
  Image,
  ListView,
  StyleSheet,
  Text,
  View,
} = React;

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 AwesomeProject = 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',
  },
  rightContainer: {
    flex: 1,
  },
  title: {
    fontSize: 20,
    marginBottom: 8,
    textAlign: 'center',
  },
  year: {
    textAlign: 'center',
  },
  thumbnail: {
    width: 53,
    height: 81,
  },
  listView: {
    paddingTop: 20,
    backgroundColor: '#F5FCFF',
  },
});

AppRegistry.registerComponent('AwesomeProject', () => AwesomeProject);
0 Answers