在 React Native 中用 Flexbox 开发落地页(翻译)

2019-03-28

前言

本文的原作者是Holly Girouard,文章源链接为 Building a Landing Page in React Native Using Flexbox,我将其翻译成中文。

如果你不熟悉 React Native 的 Flexbox 布局机制的话,编写 React Native 布局会十分痛苦。虽然你也可以用 JavaScript 写布局,但是理解和使用 Flexbox 布局是 React Native UI 开发的必备技能。在本文中,我们将使用 React Native 开发一个落地页。

关于 Flexbox

你很可能已经熟悉了 CSS 中的 Flexbox 布局,React Native 的 Flexbox 实现跟它几乎是一样的。

Flexbox 是一种高效强大的布局工具,专门用于构建用户界面,特别是移动界面。

通过在组件的 style 中使用 flex 属性,我们可以根据可用的空间动态地放大和缩小。

React 中的 flex 属性与 CSS 的有一点不同,它的工作原理更像是 CSS 中的 fr 单元,其中提供一个数值表示占据屏幕的空间比例。

因此,在 React Native 中,flex 的取值是一个整数值。

除了 flex 属性之外,另一点与 CSS 的不同是:在 React Native 的 Flexbox 中,flex-direction 默认值是 column

搞起

在我们的实例项目汇总,我们要创建一个 AlligatorChef 🐊 落地页。

项目完成后的最终结果如下:

🐥如果你是 React Native 新手,可以先看我们的 Getting Started with React Native and Expo 再看本文。

🚀 React Native 快速开始

在命令行中输入下列命令创建一个新的 React Native 项目。进入新目录中,初始化项目,并输入 i 对应 iOS 平台:

$ create-react-native-app AlligatorChef
$ cd AlligatorChef
$ npm start
$ i

现在我们有什么?

Create React Native App 的初始化代码中,已经包含了一个 Flexbox 的示例,它在 StyleSheet.create 调用中使用 flex 属性。

看一下 App.js 文件。似乎给了我们一个暗示,Flexbox 是在 React Native 中设置样式的关键:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
});

这告诉我们 container 应当拉伸到充满整个屏幕。

下一步

现在我们有了默认的项目,下一步对它做一些修改。

首先创建两个 Text 元素用于标题。

替换以下代码:

<Text>Open up App.js to start working on your app!</Text>
<Text>Changes you make will automatically reload.</Text>
<Text>Shake your phone to open the developer menu.</Text>

替换为:

<Text style={styles.h1}>AlligatorChef</Text>
<Text style={styles.h2}>Providing cajun bacon recipes since 1987.</Text>

下面再在 StyleSheet 中添加一些样式。清空 StyleSheet 中的内容,替换为以下样式:

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'space-between',
    backgroundColor: '#000',
    alignItems: 'center',
    width: '100%',
  },
  h1: {
    color: '#008F68',
    fontSize: 40,
  },
  h2: {
    color: '#FAE042',
    fontSize: 18,
    marginTop: 8,
  },
});

现在看看修改之后的效果。

我们将 justifyContentcenter 改为 space-between。如果我们改回 center,所有元素都会再次居中。

当使用 justifyContent: 'space-between'时,表示每个元素在屏幕的顶部与底部之间按照相同的间隔排布。

这是为什么我们的 h1 跑到最顶上,h2 跑到最底下。

图片和按钮

我们继续来添加按钮和图片。

将你现在的代码替换为下面代码,保留 styles 声明:

import React from 'react';
import { StyleSheet, Text, View, Button, Image } from 'react-native';
import Logo from './assets/chef.png';

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.h1}>AlligatorChef</Text>
        <Text style={styles.h2}>Providing cajun bacon recipes since 1987.</Text>
        <Image
          source={Logo}
          style={styles.image}
        />
        <Button
          title="LET'S START"
          style={styles.button}
          onPress={() => this.onPress()}
          color="#fff"
        />
      </View>
    );
  }
}

// ...

下面在根目录下创建一个 assets 目录。

通常我们把图片放到 assets 目录下,这样项目中的所有组件都能很方便地获取到。

添加一个你想用作 AlligatorChef 🐊 Logo 的图片到这个目录下。

React Native 对图片的处理有些奇怪,可能会给你带来问题。在我们的长讲下,确保你导入的是 png 图片。

添加图片后,效果过下:

这个效果不太好。

布局样式

现在界面中的元素都有了,但是展示效果不太好。我们来修复它。

React Native 中的图片需要一个尺寸

作为 React Native 的一个最佳实践,我们总是给图片一个尺寸。

如果你曾经遇到过图片没有展示出来的问题,先看看你有没有给图片指定尺寸。

在上面的代码中可以看出,我们已经给图片指定了一个 style prop。在 StyleSheet 添加如下代码:

image: {
  width: 300,
  height: 260,
  justifyContent: 'center',
},

再次运行,会看到 Logo 的展示效果好多了!

我的按钮看着不像按钮

React Native 中的按钮也很特别。

它的按钮文字不是在 <Button></Button> 之间添加,而是使用一个 title prop 添加。

下面我们继续给按钮添加一个 Style。

首先,添加一个 class 为 buttonContainer 的 View:

<View style={styles.buttonContainer}>
  <Button
    title="LET'S START"
    style={styles.button}
    onPress={() => this.onPress()}
    color="#fff"
  />
</View>

stylesheet 的定义如下:

buttonContainer: {
  backgroundColor: '#008F68',
  borderRadius: 5,
  padding: 8,
  margin: 8,
},

显示效果如下,现在看起来像按钮了:

准备来点 Flexbox 吗?!

现在我们通过使用神器的 Flexbox 让页面剩下的部分都变得可控。准备好了吗?🏀

首先我们在最初的 container 下添加三个 container。

将你的原始代码替换为下面代码,你将注意到几个新的类(topContainer, middleContainer, 和 bottomContainer):

import React from "react";
import {
  StyleSheet,
  Text,
  View,
  Button,
  Image,
  ProgressViewIOS
} from "react-native";
import Logo from "./assets/chef.png";

export default class App extends React.Component {
  render() {
    return (
      <View style={styles.container}>
        <View style={styles.topContainer}>
          <Text style={styles.h1}>AlligatorChef</Text>
          <Text style={styles.h2}>
            Providing cajun bacon recipes since 1987.
          </Text>
        </View>
        <View style={styles.middleContainer}>
          <Image source={Logo} style={styles.image} />
        </View>
        <ProgressViewIOS number={1} />
        <View style={styles.bottomContainer}>
          <View style={styles.buttonContainer}>
            <Button
              title="LET'S START"
              style={styles.button}
              onPress={() => this.onPress()}
              color="#fff"
            />
          </View>
        </View>
      </View>
    );
  }
}

下面给我们的 StyleSheet 添加这些类:

topContainer: {
  flex: 2,
  justifyContent: 'center',
  alignItems: 'center',
},
middleContainer: {
  flex: 3,
  justifyContent: 'flex-start',
  alignItems: 'center',
},
bottomContainer: {
  justifyContent: 'flex-end',
  width: '90%',
  margin: 20,
  padding: 10,
},

下面是见证奇迹的时刻:

讨论一下 Flexbox 大救星

我们给 topContainer 赋值了 flex: 2,赋予 flex-grow 值为 2,因此它占据原始 container 的 2/5 的空间。

我们还告诉他将所有子元素 align 和 justify 到中心。

在下图中给出一个可视的理解:

我们给 middleContainer 赋值 flex: 3,表示它占据原始 container 3/5 的空间。

我们没有用 justifyContent: 'center',而是使用了 justifyContent: 'flex-start'

这样 AlligatorChef 会位于 middleContainer 的顶部位置:

最后,因为我们不关心按钮占据的控件,因此就不需要给他设定 flex 值。我们只需要按钮位于页面的最底部,因此设置值 bottomContainer justifyContent: 'flex-end'

大功告成!

本文只是一个大体介绍,Flexbox 布局模块不仅在 React Native 中很有用,在整个 Web 开发中都很有用。