你好 JSX 的世界
你当前的应用程序会使用 React.createElement 来构建应用 UI ,React会将其转换到原生环境中。在当前情况下,你的JavaScript代码是完全可读的,但一个更复杂的 UI 与嵌套的元素将迅速使代码变成一大坨。
确保应用程序仍在运行,然后回到你的文本编辑器中,编辑 PropertyFinderApp.js 。修改组件 render 方法的返回语句如下:
return <React.Text style={styles.text}>Hello World (Again)</React.Text>;
这是 JSX ,或 JavaScript 语法扩展,它直接在你的 JavaScript 代码中混合了类似 HTML 的语法;如果你是一个 web 开发人员,应该对此不陌生。在本篇文章中你将一直使用 JSX 。
把你的改动保存到 PropertyFinderApp.js 中,并返回到模拟器。按下 Cmd + R ,你将看到你的应用程序刷新,并显示更新的消息 “Hello World(again)”。
重新运行一个 React Native 应用程序像刷新 web 浏览器一样简单!:]
因为你会使用相同的一系列 JavaScript 文件,您可以让应用程序一直运行,只在更改和保存 PropertyFinderApp.js 后刷新即可
注意:如果你感到好奇,可以看看你的“包”在浏览器中,JSX被转换成什么。
这个 “Hello World” 已经够大家玩耍了,是时候构建实际的应用程序了!
添加导航
我们的房产查找应用使用标准的栈式导航,基于 UIKit 的 navigation controller。现在正是添加的时候。
在 index.ios.js 文件中,把 PropertyFinderApp 重命名为HelloWorld:
class HelloWorld extends React.Component {
“Hello World” 这几个字你还需要让它显示一会儿,但它不再是应用的根组件了。
接下来,在 HelloWorld 这个组件下面添加如下这个类:
class PropertyFinderApp extends React.Component {
render() {
return (
<React.NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Property Finder',
component: HelloWorld,
}}/>
);
}
}
构造一个 navigation controller,应用一个样式,并把初始路由设为 Hello World 组件。在 Web 开发中,路由就是一种定义应用导航的一种技术,即定义页面——或者说是路由——与 URL 的对应关系。
在同一个文件中,更新样式定义,包含如下 container 的样式:
var styles = React.StyleSheet.create({
text: {
color: 'black',
backgroundColor: 'white',
fontSize: 30,
margin: 80
},
container: {
flex: 1
}
});
在随后的教程中会告诉你 flex: 1 是什么意思。
回到模拟器,Cmd+R,看看新 UI 的样子:
这就是包含了 root view 的 navigation controller,目前 root view 就是 “Hello World”。很棒——应用已经有了基础的导航结构,到添加真实 UI 的时候了。
创建搜索页
在项目中添加一个新文件,命名为 SearchPage.js,然后将其放在PropertyFinderApp.js 所在目录下。在文件中添加下面代码:
'use strict';
var React = require('react-native');
var {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
ActivityIndicatorIOS,
Image,
Component
} = React;
你会注意到,位于引入 react-native 所在位置的前面有一个严格模式标识,紧接着的声明语句是新知识。
这是一种解构赋值,准许你获取对象的多个属性并且使用一条语句将它们赋给多个变量。结果是,后面的代码中可以省略掉 React 前缀;比如,你可以直接引用 StyleSheet ,而不再需要 React.StyleSheet。解构同样适用于操作数组,更多细节请戳这里。
继续在 SearchPage.js 文件中添加下面的样式:
var styles = StyleSheet.create({
description: {
marginBottom: 20,
fontSize: 18,
textAlign: 'center',
color: '#656565'
},
container: {
padding: 30,
marginTop: 65,
alignItems: 'center'
}
});
同样,以上都是标准的 CSS 属性。和 Interface Builder 相比,这样设置样式缺少了可视化,但是比起在 viewDidLoad() 中逐个设置视图属性的做法更友好!
只需要把组件添加到样式声明的前面:
class SearchPage extends Component {
render() {
return (
<View style={styles.container}>
<Text style={styles.description}>
Search for houses to buy!
</Text>
<Text style={styles.description}>
Search by place-name, postcode or search near your location.
</Text>
</View>
);
}
}
render 很好地展示出 JSX 以及它表示的结构。通过这个样式,你可以轻易地描绘出组件 UI 的结构:一个容器,包含两个 text 标签。
最后,将下面的代码添加到文件末尾:
module.exports = SearchPage;
这可以 export SearchPage 类,方便在其他文件中使用它。
下一步是更新应用的路由,以初始化路由。
打开 PropertyFinderApp.js,在文件顶部紧接着上一个 require 语句的位置添加下面代码:
var SearchPage = require('./SearchPage');
在 PropertyFinderApp 类的 render 函数内部,通过更新 initialRoute 来引用最新添加的页面,如下:
此时,如果你愿意则可以移除 HelloWorld 类以及与它相关联的样式。你不在需要那段代码了。
切换到模拟器,按下 Cmd+R 查看新的 UI:
使用 Flexbox 定义外观现在,你已经看到了用基本的 CSS 属性来控制外间距(margin),内间距(padding)还有颜色(color)。不过,可能你还不太了解要如何使用伸缩盒(flexbox),flexbox 是最近新加入 CSS 规范,用它就能很便利地布局界面。
React Native 用 css-layout(这是一个用 JavaScript 实现flexbox标准然后编译成 C(iOS平台)或者Java(Android平台)的库)。
Facebook把这个项目单独出来实在太正确了,这样可以编译成多种语言,促进更多新颖的应用的发展,比如flexbox layout to SVG。
在你的App中,容器(container)默认地是纵向布局,也就是说在它的子元素将会竖直地排列,像这样:
这被称为主轴 (main axis),它的方向可以是竖直的也可以是水平的。每一个子元素在竖直方向上的位置是由它的margin,height和padding共同决定的。容器的 alignItems 属性也要设置成 center,这个属性可以控制子元素在十字轴上的位置。在这里,它实现了居中对齐的文本。
好啦,现在我们把输入框和按钮加上去吧。打开 SearchPage.js,将下面的代码插入第二个 Text 元素的后面:
<View style={styles.flowRight}>
<TextInput
style={styles.searchInput}
placeholder='Search via name or postcode'/>
<TouchableHighlight style={styles.button}
underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Go</Text>
</TouchableHighlight>
</View>
<TouchableHighlight style={styles.button}
underlayColor='#99d9f4'>
<Text style={styles.buttonText}>Location</Text>
</TouchableHighlight>
现在你已经加上了两个最高等级的视图(top-level view),一个视图包含了文本输入框和一个按钮,还有一个视图内只有一个按钮。在后文中你会看到,它们的样式是什么样的。
接着,添加上对应的样式:
flowRight: {
flexDirection: 'row',
alignItems: 'center',
alignSelf: 'stretch'
},
buttonText: {
fontSize: 18,
color: 'white',
alignSelf: 'center'
},
button: {
height: 36,
flex: 1,
flexDirection: 'row',
backgroundColor: '#48BBEC',
borderColor: '#48BBEC',
borderWidth: 1,
borderRadius: 8,
marginBottom: 10,
alignSelf: 'stretch',
justifyContent: 'center'
},
searchInput: {
height: 36,
padding: 4,
marginRight: 5,
flex: 4,
fontSize: 18,
borderWidth: 1,
borderColor: '#48BBEC',
borderRadius: 8,
color: '#48BBEC'
}
要注意格式问题:每一个样式都是用逗号分隔开的,所以别忘了在container 选择器后面还要加上一个逗号。
以上的样式将会应用在你刚刚加上的输入框和按钮上。
现在返回到模拟器,然后按下 Cmd+R 刷新界面:
文本区域和 ’Go’ 按钮在同一行,不需要显式地定义两个组件的宽度,你只需要将它们放在同一个容器中,加上 flexDirection:’row’ 样式,再定义好它们的 flex 值。文本区域是 flex:4,按钮则是 flex:1,这说明两者的宽度比是4:1。大概你也发现了,你的“按钮”其实并不是按钮!:] 使用了 UIKit 后,按钮更倾向于是可以轻碰(tap)的标签(label),所以 React Native 团队决定直接在 JavaScript 中构建按钮了。所以你在 App 中使用的按钮是 TouchableHighlight,这是一个 React Native 组件,当轻碰 TouchableHighlight 时,它会变得透明从而显示出衬底的颜色(也就是按钮下层的组件颜色)。
搜索界面的最后一步就是加上一张图片.你可以从这里下载我们用的图片素材并解压。
在Xcode中打开Images.xcassets文件,点击加号添加一个新的图片集。然后将图片素材拖进正确的“区间”:
你需要重启应用才能让图片生效。
将以下代码添加到 TouchableHighlight 组件后面,它将用于“获取位置”按钮:
<Image source={require('image!house')} style={styles.image}/>
现在再样式表的最后加上图片对应的样式,别忘了给原样式中最后一个加上逗号哦:
image: {
width: 217,
height: 138
}
require(‘image!house’) 语句用于确定在你应用的asset目录下的图片资源,在 Xcode 中,如果你的打开了 Images.xcassets,你会看到一个“房屋”的图标,正是上面代码中引用到的。
返回到模拟器,Cmd+R刷新UI:
注意:如果你这会没有看到“房屋”图片,取而代之的是一张“找不到资源”的图片,尝试重启packager(也就是在终端里输入 npm start 命令)。
现在你的应用看起来挺不错的啦,不过它还少了点功能。接下来你的任务就是给它加上点状态,让它执行一些操作。