From 7c517e061cf51267f7fab6cd449a73e67fd3265b Mon Sep 17 00:00:00 2001 From: Hstarorg Date: Fri, 23 Mar 2018 23:38:31 +0800 Subject: [PATCH] Update chapter1 Former-commit-id: 624fa3bbda209fdb93bd9e835cbb6fc26b78503b Former-commit-id: 018e6f4014fc5f5e9b3075cb911483a9df19019b --- .../\347\253\240\350\212\202\344\270\200.md" | 315 ++++++++++++++++++ 1 file changed, 315 insertions(+) diff --git "a/React Native Cookbook/\347\253\240\350\212\202\344\270\200.md" "b/React Native Cookbook/\347\253\240\350\212\202\344\270\200.md" index 6a6c067..bad728a 100644 --- "a/React Native Cookbook/\347\253\240\350\212\202\344\270\200.md" +++ "b/React Native Cookbook/\347\253\240\350\212\202\344\270\200.md" @@ -450,8 +450,323 @@ AppRegistry.registerComponent('ButtonsAndEvents', () => MainApp); ## 显示列表元素 +**列表无处不在!**如用户的历史订单列表、商店中的可用商品、待播放的歌曲列表等;基本上,任何 App 都需要用到列表。 + +在这个小节中,我们将使用列表组件显示几个 Item。先将一些数据定义成 JSON 文件,然后使用 `require` 来加载这个文件,最终将其渲染为一个漂亮又简洁的列表布局。 + +### 前提 + +首先,创建一个名为 `ListItems` 的空 App。为了在列表的每个 Item 中显示 icon 图标,请下载图片资源或使用您自己的 `.png` 图片资源。 + +### 实现 + +1. 在项目中创建 `src` 目录,然后在 `src` 目录中创建 `MainApp.js` 以及 `sales.json`: + +2. 我们将要显示的列表数据定义在 `sales.json` 中,示例数据如下: + +```json +[{ "items": 5, "address": "140 Broadway, New York, NY 11101", "total": 38, "date": "May 15, 2016" }] +``` + +3. 为了避免占用大量篇幅,我只定义了一条数据,您可以往数据数组中加入更多内容。使用 `Ctrl+C Ctrl+V` 来创建更多元素,另外,可以修改其中的一些数值。 + +4. 找到 `index.ios.js` 和 `index.android.js` 文件,移除掉已有的代码,添加如下代码来导入依赖和注册 App: + +```js +import React, { Component } from 'react'; +import { AppRegistry } from 'react-native'; +import MainApp from './src/MainApp'; +AppRegistry.registerComponent('ListItems', () => MainApp); +``` + +5. 在上一步中,虽然导入了 `MainApp`,但实际上并没有定义。打开 `src/MainApp.js`,然后导入如下依赖: + +```js +import React, { Component } from 'react'; +import { StyleSheet, View, ListView, Image, Text } from 'react-native'; +import data from './sales.json'; +const basketIcon = require('./images/basket.png'); +``` + +6. 现在需要创建一个类来渲染列表。将销售数据放到 `state` 中,可以让我们很轻松的插入或删除数据: + +```js +class MainApp extends Component { + constructor(props) { + super(props); + var ds = new ListView.DataSource({ + rowHasChanged: (r1, r2) => r1 !== r2 + }); + this.state = { + dataSource: ds.cloneWithRows(data) + }; + } + renderRow(record) { + // Defined on step 8 + } + render() { + // Defined on step 7 + } +} +export default MainApp; +``` + +7. 我们需要在 `render` 方法中,使用 `ListView` 组件,并使用 `renderRow` 方法来渲染每个单独的 Item。`dataSource` 属性定义了需要在列表中呈现的数组元素: + +```js +render() { + return ( + + Sales + + + ); +} +``` + +8. 现在,我们来补充 `renderRow` 方法的内容。这个方法接收包含所需信息单个对象。我们将要显示三个数据列。第一列中,显示 Icon 图标,第二列中每次销售的商品数量和该订单的发货地址,第三列中将售出日期和总价: + +```js +renderRow(record) { + return ( + + + + + + {record.items} Items + {record.address} + + + {record.date} + ${record.total} + + + ); +} +``` + +9. 当我们定义好 JSX 之后,是时候添加样式了。首先,需要为主容器、标题以及行容器定义颜色、外边距、内边距等等。为了为每个列创建三列布局,需要使用到 `flexDirection` 中的 `row` 属性。我们将在本章其他小节中学到更多关于该属性的知识: + +```js +const styles = StyleSheet.create({ + mainContainer: { + flex: 1, + backgroundColor: '#fff' + }, + title: { + backgroundColor: '#0f1b29', + color: '#fff', + fontSize: 18, + fontWeight: 'bold', + padding: 10, + paddingTop: 40, + textAlign: 'center' + }, + row: { + borderColor: '#f1f1f1', + borderBottomWidth: 1, + flexDirection: 'row', + marginLeft: 10, + marginRight: 10, + paddingTop: 20, + paddingBottom: 20 + } +}); +``` + +10. 当我们刷新模拟器时,我们可以看到如下一些截图: + +11. 现在,在 `StyleSheet` 的定义中,我们来给 icon 图标添加样式。添加一个黄色圆圈作为背景,并将图标的颜色设定为白色: + +```js +iconContainer: { + alignItems: 'center', + backgroundColor: '#feb401', + borderColor: '#feaf12', + borderRadius: 25, + borderWidth: 1, + justifyContent: 'center',height: 50, + width: 50, +}, +icon: { + tintColor: '#fff', + height: 22, + width: 22, +}, +``` + +12. 应用这些变更后,我们可以每行的左侧有一个好看的 icon 图标,如下图所示: + +13. 最后,为文本设置样式。需要设置颜色、字体、字号、内边距以及其他一些样式: + +```js +info: { + flex: 1, + paddingLeft: 25, + paddingRight: 25, +}, +items: { + fontWeight: 'bold', + fontSize: 16, + marginBottom: 5, +}, +address: { + color: '#ccc', + fontSize: 14, +}, +total: { + width: 80, +}, +date: { + fontSize: 12, + marginBottom: 5, +}, +price: { + color: '#1cad61', + fontSize: 25, + fontWeight: 'bold', +}, +``` + +14. 最终结果应该类似下图所示: + +### 原理分析 + +在步骤 6 中,创建了数据源并添加到了 `state` 中。`ListView.DataSource` 为 `ListView` 组件提供了高性能的数据处理。`rowhHasChanged` 是一个必须的属性,它是一个用来比较数据元素是否变化的函数。 + +我们需要调用 `cloneWithRows` 来将数据填充到数据源,并传递给组件。 + +如果要添加更多数据,我们应该再次调用 `cloneWithRows` 包含旧数据和新数据。数据源将计算差异并在必要时重新渲染。 + +在步骤 7 中,使用 `render` 方法来渲染列表。步骤 6 中的数据源以及 `renderRow` 方法是两个必备的属性。 + +`renderRow` 是为每个行返回 JSX 的函数。 + +### 更多 + +我们使用 `flexbox` 创建了一个简单的布局;然而,在本章中还有另外的小节将深入的探讨使用 `flexbox` 的更多细节。 + +一旦有了列表,我们就应该有机会去看到每个订单的细节。此时,我们应该使用 `TouchableHighlight` 组件作为每行的主容器,所以,请继续尝试。如果还不确定如何使用 `TouchableHighlight` 组件,请参考本章节中“创建一个切换按钮”。 + ## 在视窗中添加选项卡 +`Tabs(选项卡)` 是一个非常常见的组件,特别是在 `iOS` App 中。在本小节中,我们来学习如何在 `iOS` 中使用选项卡组件。截止目前,还并不支持 `Android`,如果真的需要在 `Android` 中使用选项卡,请选用第三方组件库来添加类似功能。 + +### 实现 + +1. 首先,导入该组件的所有依赖项以及需要使用到的图标: + +```js +import React, { Component } from 'react'; +import { StyleSheet, View, Image, Text, TabBarIOS } from 'react-native'; +const homeIcon = require('./images/home.png'); +const favIcon = require('./images/star.png'); +const blogIcon = require('./images/notebook.png'); +const profileIcon = require('./images/user.png'); +``` + +2. 为了选中一个选项卡,应该在 `state` 中存储当前选中状态,因此我们需要使用类来定义组件,如下: + +```js +class MainApp extends Component { + state = { + selected: 'home' + }; + selectTab(id) { + // Defined on step 5 + } + renderTab(options) { + // Defined on step 4 + } + render() { + // Defined on step 3 + } +} +``` + +3. 在 `render` 方法中,我们需要定义选项卡组件以及我们要展示的每个选项卡。此时,我们可以使用包含参数的 `renderTab` 方法来构建 JSX,这使得我们可以通过调用函数来复用代码: + +```js +render() { + return ( + + {this.renderTab({title: 'Home', id: 'home', icon: homeIcon})} + {this.renderTab({title: 'Favorites', id: 'favorites', icon: favIcon})} + {this.renderTab({title: 'Blog', id: 'blog', icon: blogIcon})} + {this.renderTab({title: 'Profile', id: 'profile', icon: profileIcon})} + + ); +} +``` + +4. 对于 `renderTab` 方法,我们需要定义一些属性,如标签标题、图标、是否选中以及选中时的回调函数。现在,我们为每个标签设定相同的内容,实际应用中,需要将主要内容作为参数传递到 App 中。其中最重要的属性就是选中属性。因为只可以在选项卡中选中一个项,所以将当前选中的项保存在 `state` 中: + +```js +renderTab(options) { + return ( + this.selectTab(options.id)} + icon={options.icon} + > + + + {options.title} + + + ); +} +``` + +5. 在上一步中,在选项卡项按下时,将会调用 `selectTab` 方法。这里的考虑是,当用户按下选项卡项时,调用此函数把选中项 ID 保存到 `state` 中,用来设置当前选中状态: + +```js +selectTab(id) { + this.setState({ + selected: id, + }); +} +``` + +6. 接着,给屏幕上的内容添加一些样式,也为每个标签的 icon 设置合适的颜色。同时,我们也将组件进行导出,便于在其他任何地方使用: + +```js +const styles = StyleSheet.create({ + container: { + flex: 1, + alignItems: 'center', + justifyContent: 'center' + }, + title: { + fontSize: 20, + marginTop: 20 + }, + icon: { + width: 30, + height: 30, + tintColor: '#42b49a' + } +}); +export default MainApp; +``` + +7. 最后,更新 `index.ios.js`,导入我们新的类: + +```js +import React, { Component } from 'react'; +import { AppRegistry } from 'react-native'; +import MainApp from './src/MainApp'; +AppRegistry.registerComponent('TabsComponent', () => MainApp); +``` + +8. 在模拟器中查看最终效果,如下: + ## 使用 `flexbox` 创建个人资料页面 ## 设置导航器