使用 JavaScript 构建原生应用实战篇(三)

genie.github.io
再上一篇文章我们已经学会了如何利用ReactJs Native制作我们的入门hello world应用,掌握来ReactJs Native应用的运行和调试方式。从这篇文章开始,我们真正的开始构建实际的应用程序。

我们的汉字学习卡片应用使用IOS标准的栈式导航,基于 UIKit 的 navigation controller。ReacctJs针对这个原生组件进行了包装,在类库里面的名字叫做NavigatorIOS,让我们首先把它添加到我们的应用中。

index.ios.js 文件中,把上一篇文章中新建的类Hanzi 重命名为HelloWorld

“Hello World” 这几个字你还需要让它显示一会儿,但它不再是应用的根组件了。

接下来,在 HelloWorld 这个组件下面添加如下这个类:

1
2
3
4
5
6
7
8
9
10
11
12
class Hanzi extends React.Component {
render() {
return (
<React.NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Property Finder',
component: HelloWorld,
}}/>

);

}
}

构造一个navigation controller,应用一个样式,并把初始路由设为 Hello World 组件。在 Web开发中,路由就是一种定义应用导航的一种技术,即定义页面——或者说是路由——与 URL 的对应关系。

在同一个文件中,更新样式定义,包含如下 container 的样式:

1
2
3
4
5
6
7
8
9
10
11
var styles = React.StyleSheet.create({
text: {
color: 'black',
backgroundColor: 'white',
fontSize: 30,
margin: 80
},
container: {
flex: 1
}
});

注意这个flex属性点用法。相信你已经看到了用基本的 CSS 属性来控制外间距(margin),内间距(padding)还有颜色(color)。不过,可能你还不太了解要如何使用弹性盒子属性(flexbox),flexbox 是新增的 CSS3 规范,用它就能很便利地布局界面。但是React Native上对其的实现和标准还是有一点不同。如果你感兴趣可以参考相关的文档和链接。不过不用着急,我们在后面的文章中也会讲到它的详细用法。

你可以在xcode中开启模拟器,看看应用加上了导航栏的新UI的样子。

这就是包含了 root view 的导航栏控制器,目前 root view 就是 “Hello World”。应用已经有了基础的导航结构,到添加真实 UI 的时候了。

创建列表页

为简单起见,我们所有的代码都放到一个文件中。回到文件的顶部,我们先引入需要的一些包含在React系统类库中的变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
'use strict';

var React = require('react-native');
var {
StyleSheet,
Text,
TextInput,
View,
ListView,
TouchableHighlight,
ActivityIndicatorIOS,
Image,
Component,
NavigatorIOS
} = React;

你会注意到,位于引入react-native 所在位置的前面有一个严格模式标识,紧接着的声明语句是新知识。

这是一种解构赋值,准许你获取对象的多个属性并且使用一条语句将它们赋给多个变量。解构同样适用于操作数组,更多细节请戳这里
这样声明的好处是在接下来的代码中可以省略掉 React 前缀;比如,你可以直接引用 StyleSheet ,而不再需要 React.StyleSheet。

然后继续添加如下的UI样式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
var styles = StyleSheet.create({
description: {
marginBottom: 20,
fontSize: 18,
textAlign: 'center',
color: '#656565'
},
container: {
padding: 30,
marginTop: 65,
alignItems: 'center'
},
row : {
margin:20,
width: 320,
flexDirection: 'row',
alignItems: 'center',
alignSelf: 'stretch'
},
header: {
marginTop: 64,
padding: 30,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#29DF9A',
},
welcomeMsg: {
fontSize: 55,
color: '#FFF',
fontWeight: 'bold',
marginBottom:20,
},
getStart: {
fontSize: 15,
fontWeight: 'bold',
color: '#FFF'
},
separator: {
height: 1,
backgroundColor: '#dddddd'
},
status: {
fontSize: 15,
flex: 1,
color: '#29DF9A'
},
title: {
flex: 4,
fontSize: 20,
color: '#656565'
}
});

添加的同时,不要忘记或者多添加了每个样式之间的逗号。同样,以上都是标准的 CSS 属性。和 Interface Builder 相比,这样设置样式缺少了可视化,但是比起在 viewDidLoad()中逐个设置视图属性的做法更友好!感觉就像是在编写CSS文件,痛快淋漓!要记住的就是CSS里面的双字节属性都要改成驼峰法命名。我们再一次的用到了flex属性,与此同时的还有flexDirection、justifyContent和alignItems属性,它们之间时协同工作的!

找到之前的HelloWorld类,我们现在需要重新把它命名为StudyListScreen,然后添加如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
class StudyListScreen extends Component {

constructor(props) {
super(props);
var dataSource = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1.guid !== r2.guid
});
var listData = [
{title: 'Animals', status: '96%'},
{title: 'Time', status: '50%'},
{title: 'Actions', status: 'study'},
{title: 'Places', status: 'study'},
{title: 'Plants', status: 'study'},
{title: 'Foods', status: 'study'}
];
this.state = {
dataSource: dataSource.cloneWithRows(listData)
};
}
renderRow(rowData, sectionID, rowID) {
return (
<View>
<TouchableHighlight
underlayColor='#84EFC6'>

<View style={styles.row}>
<Text style={styles.title}>{rowData.title}</Text>
<Text style={styles.status}>{rowData.status}</Text>
</View>
</TouchableHighlight>
<View style={styles.separator}/>
</View>
);

}
render() {
return (
<View >
<View style={styles.header}>
<Text style={styles.welcomeMsg}>你好</Text>
<Text style={styles.getStart}>Let us get started!</Text>
</View>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
keyboardDismissMode="on-drag"
keyboardShouldPersistTaps={true}
automaticallyAdjustContentInsets={false}
showsVerticalScrollIndicator={true}/>

</View>

);

}
}

我们除了常规的render()方法外,还用到了一个constructor构造函数,里面定义了listData变量和初始状态,用来暂时模拟列表数据。在render()方法里面,我们定义了一个ListView的组件,用来展示我们的学习列表。renderRow函数在ListView的属性里面用到,用于定义列表的每个item如何生成和展示。

1
renderRow={this.renderRow.bind(this)}

最后,修改我们的主应用类Hanzi,讲里面的初始路由设置成我们新定义的这个UI组件。

1
2
3
4
5
6
7
8
9
10
11
12
class Hanzi extends Component {
render() {
return (
<NavigatorIOS
style={styles.nav}
initialRoute={{
title: 'Hanzi',
component: StudyListScreen,
}}/>

);

}
}

现在返回到模拟器,然后按下 Cmd+R 刷新界面。不要被效果惊艳到哦😏!
genie.github.io

好吧,今天的课程就到这里,下一篇文章在继续吧!