如何用 React Native 创建一个iOS APP?(三)
前两部分,《如何用 React Native 创建一个iOS APP?》,《如何用 React Native 创建一个iOS APP (二)?》中,我们分别讲了用 React Native 来创建 Navigation Bar,Tab Bar 等这些控件,今天在第三节,我们着重讲一下剩下的一些控件。闲话少叙,我们直入主题!
添加一个ListView
React Native 有一个叫做 ListView 的组件,可以显示滚动的行数据,基本上是 ios 项目上的一个术语表视图。
首先,按照所显示的修改解构的声明以包含多个组件,然后就可以使用。
var {
Image,
StyleSheet,
Text,
View,
Component,
ListView,
TouchableHighlight
} = React;
添加以下风格样式表:
separator: {
height: 1,
backgroundColor: '#dddddd'
}
添加以下BookList类构造函数:
constructor(props) {
super(props);
this.state = {
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
然后添加以下功能:
componentDidMount() {
var books = FAKE_BOOK_DATA;
this.setState({
dataSource: this.state.dataSource.cloneWithRows(books)
});
}
在构造函数中,我们创建一个列表视图。数据源对象,并将其分配给数据源属性。列表视图使用的数据源是一个接口,可以确定更新了的 UI 改变所在的行。我们提供一个函数来比较双行的同一性,它可以用来决定数据列表的改变。
当组件加载/安装到用户界面视图时 componentDidMount() 便被调用。当这个函数被调用时,我们可以从我们的数据对象中设置数据源属性。
修改 render() 函数如下图所示:
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderBook.bind(this)}
style={styles.listView}
/>
);
}
接下来添加以下书目类函数:
renderBook(book) {
return (
<TouchableHighlight>
<View>
<View style={styles.container}>
<Image
source={{uri: book.volumeInfo.imageLinks.thumbnail}}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title}>{book.volumeInfo.title}</Text>
<Text style={styles.author}>{book.volumeInfo.authors}</Text>
</View>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
以上创建了一个在 render() 中的列表视图组件呈现。这是datasource 属性设置为数据源的值,我们前面定义的函数renderBook() 呈现 ListView 的行。
在 renderBook() 我们使用 TouchableHighlight 组件。这是一个包装器进行观点正确的响应触摸。在低压下,包装视图的透明度降低,使得衬底的颜色显示变暗或视图着色。如果你压在一个列表视图,你将看到突出的颜色,就像我们先前选择一个表视图单元格一样。添加一个空视图组件底部的行分隔符的样式。这种视图将只是一个灰色水平线,就像每一行之间的一个分区。
重新加载应用程序,你应该看到只有一个细胞的表视图。
接下来把真实的数据加载到应用程序。
从文件中删除FAKE—BOOK—DATA变量,添加以下数据来代替它。这是我们从数据中加载的 URL。
var REQUEST_URL = 'https://www.googleapis.com/books/v1/volumes?q=subject:fiction';
修改 destructuring 声明。
var {
Image,
StyleSheet,
Text,
View,
Component,
ListView,
TouchableHighlight,
ActivityIndicatorIOS
} = React;
添加以下程序:
listView: {
backgroundColor: '#F5FCFF'
},
loading: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
构造函数修改如图所示。我们将另一个属性添加到组件的状态对象。我们通过这个来判断是否加载视图。
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
修改 componetDidMount() 函数如图所示,添加如下 fetchData() 函数。fetchData() 调用Googlebooks API 并且用从响应得到的数据设置数据源属性。它也把 isLoading 设置为 true。
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.items),
isLoading: false
});
})
.done();
}
按提示修改渲染()函数,添加如下 renderLoading 函数。我们为isLoading 添加一个检查系统,如果它设置为 true,我们就要返回被renderLoadingView() 视图返回来的视图。这将是一个视图显示一个活动指标(转子)与文本“加载书籍...”。加载完成后,你就会看到一个表中的书籍列表。
render() {
if (this.state.isLoading) {
return this.renderLoadingView();
}
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderBook.bind(this)}
style={styles.listView}
/>
);
}
renderLoadingView() {
return (
<View style={styles.loading}>
<ActivityIndicatorIOS
size='large'/>
<Text>
Loading books...
</Text>
</View>
);
}
重新加载应用程序,应该出现如下所示:
添加 Detail View
如果你点击表中的一个细胞,细胞将突出显示,但并不会有什么反应。我们将添加一个可以显示我们选择这本书的详细信息的细节视图。
将文件添加到项目并命名为 BookDetail.js。把以下内容粘贴到文件中。
'use strict';
var React = require('react-native');
var {
StyleSheet,
Text,
View,
Component,
Image
} = React;
var styles = StyleSheet.create({
container: {
marginTop: 75,
alignItems: 'center'
},
image: {
width: 107,
height: 165,
padding: 10
},
description: {
padding: 10,
fontSize: 15,
color: '#656565'
}
});
class BookDetail extends Component {
render() {
var book = this.props.book;
var imageURI = (typeof book.volumeInfo.imageLinks !== 'undefined') ? book.volumeInfo.imageLinks.thumbnail : '';
var description = (typeof book.volumeInfo.description !== 'undefined') ? book.volumeInfo.description : '';
return (
<View style={styles.container}>
<Image style={styles.image} source={{uri: imageURI}} />
<Text style={styles.description}>{description}</Text>
</View>
);
}
}
module.exports = BookDetail;
我们将通过上面代码中的大多数所以不用全部浏览。我们没见过的是用道具的使用属性来提取数据。我们将通过道具属性设置传递数据到这个类。在上面,我们得到这个数据并用它来填充视图。
请注意我们在顶部边距设定一个容器。如果你不这样视图将会从屏幕顶端开始,这很可能导致一些元素被导航栏隐藏。
在 BookList.js 中添加以下程序:
var BookDetail = require('./BookDetail');
修改渲染()函数中的 TouchableHightlight 书目类如下图所示:
<TouchableHighlight onPress={() => this.showBookDetail(book)} underlayColor='#dddddd'>
当行被压缩时上述指定一个可能被命名的回调函数。把以下函数粘贴到类函数。这将推动 BookDetail 视图到导航堆栈,设置出现在导航栏中的标题栏。它通过这本书的对象对应于BookDetail类的特定行。
showBookDetail(book) {
this.props.navigator.push({
title: book.volumeInfo.title,
component: BookDetail,
passProps: {book}
});
}
重新加载应用程序,这时你应该能够看到所选书的细节。
Searching
既然我们已经完成了特色的主从复合结构的视图选项卡,我们将在搜索选项卡操作以允许用户查询 API 对书籍的选择。
打开 SearchBooks.js 并做如图修改。
use strict';
var React = require('react-native');
var SearchResults = require('./SearchResults');
var {
StyleSheet,
View,
Text,
Component,
TextInput,
TouchableHighlight,
ActivityIndicatorIOS
} = React;
var styles = StyleSheet.create({
container: {
marginTop: 65,
padding: 10
},
searchInput: {
height: 36,
marginTop: 10,
marginBottom: 10,
fontSize: 18,
borderWidth: 1,
flex: 1,
borderRadius: 4,
padding: 5
},
button: {
height: 36,
backgroundColor: '#f39c12',
borderRadius: 8,
justifyContent: 'center',
marginTop: 15
},
buttonText: {
fontSize: 18,
color: 'white',
alignSelf: 'center'
},
instructions: {
fontSize: 18,
alignSelf: 'center',
marginBottom: 15
},
fieldLabel: {
fontSize: 15,
marginTop: 15
},
errorMessage: {
fontSize: 15,
alignSelf: 'center',
marginTop: 15,
color: 'red'
}
});
class SearchBooks extends Component {
constructor(props) {
super(props);
this.state = {
bookAuthor: '',
bookTitle: '',
isLoading: false,
errorMessage: ''
};
}
render() {
var spinner = this.state.isLoading ?
( <ActivityIndicatorIOS
hidden='true'
size='large'/> ) :
( <View/>);
return (
<View style={styles.container}>
<Text style={styles.instructions}>Search by book title and/or author</Text>
<View>
<Text style={styles.fieldLabel}>Book Title:</Text>
<TextInput style={styles.searchInput} onChange={this.bookTitleInput.bind(this)}/>
</View>
<View>
<Text style={styles.fieldLabel}>Author:</Text>
<TextInput style={styles.searchInput} onChange={this.bookAuthorInput.bind(this)}/>
</View>
<TouchableHighlight style={styles.button}
underlayColor='#f1c40f'
onPress={this.searchBooks.bind(this)}>
<Text style={styles.buttonText}>Search</Text>
</TouchableHighlight>
{spinner}
<Text style={styles.errorMessage}>{this.state.errorMessage}</Text>
</View>
);
}
bookTitleInput(event) {
this.setState({ bookTitle: event.nativeEvent.text });
}
bookAuthorInput(event) {
this.setState({ bookAuthor: event.nativeEvent.text });
}
searchBooks() {
this.fetchData();
}
fetchData() {
this.setState({ isLoading: true });
var baseURL = 'https://www.googleapis.com/books/v1/volumes?q=';
if (this.state.bookAuthor !== '') {
baseURL += encodeURIComponent('inauthor:' + this.state.bookAuthor);
}
if (this.state.bookTitle !== '') {
baseURL += (this.state.bookAuthor === '') ? encodeURIComponent('intitle:' + this.state.bookTitle) : encodeURIComponent('+intitle:' + this.state.bookTitle);
}
console.log('URL: >>> ' + baseURL);
fetch(baseURL)
.then((response) => response.json())
.then((responseData) => {
this.setState({ isLoading: false});
if (responseData.items) {
this.props.navigator.push({
title: 'Search Results',
component: SearchResults,
passProps: {books: responseData.items}
});
} else {
this.setState({ errorMessage: 'No results found'});
}
})
.catch(error =>
this.setState({
isLoading: false,
errorMessage: error
}))
.done();
}
}
module.exports = SearchBooks;
在上面我们在构造函数中设置一些属性:bookAuthor,bookTitle,isLoading 和errorMessage 。很快我们将看到如何使用他们。
在render()方法中,我们检查如果 isLoading 是真的,如果确实是创建一个活动指标,否则,我们就创建了一个空的观点。以后将会用的到。
然后我们创建一个用于插入查询的搜索表单。Texinput 用于输入。我们为每个 Texinput 组件指定一个回调函数时,当用户键入一些文本时将调用该组件的值。命名时,回调函数 bookTileinput() 和bookAuthorinput() 将设置 bookAuthor和bookTlie 的状态属性和用户输入数据。当用户按下搜索按钮时 searchBooks() 就被命名了。
注意 React Native 没有一个按钮组件。相反,我们使用TouchableHighlight 并把它补充在文本周围,然后其造型就像是一个按钮。搜索按钮被按下时,根据输入的数据构造一个 URL。用户可以通过搜索标题或作者来检索,或即通过标题又通过作者来检索。如果返回结果,SearchResults 将被推到导航堆栈否则将显示一条错误消息。我们还将通过 SearchResults 类响应数据。
创建一个名为 SearchResults.js 文件并把以下程序粘贴进去。
'use strict';
var React = require('react-native');
var BookDetail = require('./BookDetail');
var {
StyleSheet,
View,
Text,
Component,
TouchableHighlight,
Image,
ListView
} = React;
var styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center'
},
title: {
fontSize: 20,
marginBottom: 8
},
author: {
color: '#656565'
},
separator: {
height: 1,
backgroundColor: '#dddddd'
},
listView: {
backgroundColor: '#F5FCFF'
},
cellContainer: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
padding: 10
},
thumbnail: {
width: 53,
height: 81,
marginRight: 10
},
rightContainer: {
flex: 1
}
});
class SearchResults extends Component {
constructor(props) {
super(props);
var dataSource = new ListView.DataSource(
{rowHasChanged: (row1, row2) => row1 !== row2});
this.state = {
dataSource: dataSource.cloneWithRows(this.props.books)
};
}
render() {
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderBook.bind(this)}
style={styles.listView}
/>
);
}
renderBook(book) {
var imageURI = (typeof book.volumeInfo.imageLinks !== 'undefined') ? book.volumeInfo.imageLinks.thumbnail : '';
return (
<TouchableHighlight onPress={() => this.showBookDetail(book)}
underlayColor='#dddddd'>
<View>
<View style={styles.cellContainer}>
<Image
source={{uri: imageURI}}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title}>{book.volumeInfo.title}</Text>
<Text style={styles.author}>{book.volumeInfo.authors}</Text>
</View>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
showBookDetail(book) {
this.props.navigator.push({
title: book.volumeInfo.title,
component: BookDetail,
passProps: {book}
});
}
}
module.exports = SearchResults;
我们已经在以上我们使用的代码中浏览了很多,所以我不会陷入每一个细节。上面得到的数据通过道具属性传递到类并创建一个 ListView 视图的数据填充。
API 中我们注意到一件事是,当你通过作者检索时,一些结果不会记录数据但数据在作者本身。这意味着对于一些行 book,volumelnfo,imageLinks 的描述会有未定义的值。因此我们要做一个检查,表明一个空的图像视图没有是否有图像,如果不做检查应用程序在加载图片时可能会本行奔溃。
我们使用之前创建的相同的 BookDetail 组件来显示每本书的细节。我们应该把上面的检查缺失的数据打包并试图加载 BookDetail 视图与缺失的数据。打开 BookDetail.js,修改 render() 函数如图所示。它用来检查数据传入是否有一个图像和在检查传入数据之前的描绘填充视图。如果我们试图描绘一本没有图片和简介的书,各自的区域将是空白一片。你可能想把一个错误的信息强加给用户,但当它在这里时我们会不理会它。
render() {
var book = this.props.book;
var imageURI = (typeof book.volumeInfo.imageLinks !== 'undefined') ? book.volumeInfo.imageLinks.thumbnail : '';
var description = (typeof book.volumeInfo.description !== 'undefined') ? book.volumeInfo.description : '';
return (
<View style={styles.container}>
<Image style={styles.image} source={{uri: imageURI}} />
<Text style={styles.description}>{description}</Text>
</View>
);
}
重新加载应用程序,你应该能够搜索一本书。
结论
虽然它仍然是一个工作正在进行中,React Native 看起来很有希望作为另一种选择构建移动应用程序。它开启了大门,对于Web 开发人员来说,让他们能够参与到移动开发的大潮;对于移动开发者,它可以提供一种方法来简化他们的开发流程。
尽管Native开发成本更高,但现阶段 Native 仍然是必须的,因为 Web的用户体验仍无法超越 Native:
Native的原生控件有更好的体验;
Native有更好的手势识别;
Native有更合适的线程模型,尽管Web Worker可以解决一部分问题,但如图像解码、文本渲染仍无法多线程渲染,这影响了 Web 的流畅性。
“学习一次,写的任何地方”。仅这一点就可能使其值得学习如何使用框架。
想要了解关于 React Native 更多的内容,你可以看下面的视频,也可以参考文档。
以上这些仅供参考,你可以在[这里](https://github.com/appcoda/React-Native-Demo-App)下载 Xcode 项目。
OneAPM Mobile Insight,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客。
本文转自 OneAPM 官方博客
如何用 React Native 创建一个iOS APP?(三)的更多相关文章
- 如何用 React Native 创建一个iOS APP?(二)
我们书接上文<如何用 React Native 创建一个iOS APP?>,继续来讲如何用 React Native 创建一个iOS APP.接下来,我们会涉及到很多控件. 1 AppRe ...
- 如何用 React Native 创建一个iOS APP?
诚然,React Native 结合了 Web 应用和 Native 应用的优势,可以使用 JavaScript 来开发 iOS 和 Android 原生应用.在 JavaScript 中用 Reac ...
- 利用react native创建一个天气APP
我们将构建一个实列程序:天气App,(你可以在react native 中创建一个天气应用项目),我们将学习使用并结合可定义模板(stylesheets).盒式布局(flexbox).网络通信.用户输 ...
- React Native创建一个APP
React Native 结合了 Web 应用和 Native 应用的优势,可以使用 JavaScript 来开发 iOS 和 Android 原生应用.在 JavaScript 中用 React 抽 ...
- React Native之(支持iOS与Android)自定义单选按钮(RadioGroup,RadioButton)
React Native之(支持iOS与Android)自定义单选按钮(RadioGroup,RadioButton) 一,需求与简单介绍 在开发项目时发现RN没有给提供RadioButton和Rad ...
- Cordova之如何用命令行创建一个项目(完整示例)
原文:Cordova之如何用命令行创建一个项目(完整示例) 1. 创建cordova项目 (注意:当第一次创建或编译项目的时候,可能系统会自动下载一些东西,需要一些时间.) 在某个目录下创建cordo ...
- 创建一个wx.App的子类
#_author:来童星#date:2019/12/20#创建一个wx.App的子类import wxclass App(wx.App): #初始化方法 def OnInit(self): frame ...
- 封装 React Native 原生组件(iOS / Android)
封装 React Native 原生组件(iOS / Android) 在 React Native中,有很多种丰富的组件了,例如 ScrollView.FlatList.SectionList.Bu ...
- NodeJS笔记(五) 使用React Native 创建第一个 Android APP
参考:原文地址 几个月前官方推出了快速创建工具包,由于对React Native不熟悉这里直接使用这2个工具包进行创建 1. create-react-native-app(下文简称CRNA): 2. ...
随机推荐
- FileZilla命令行实现文件上传以及CreateProcess实现静默调用
应用需求: 用户在选择渲染作业时面临两种情况:一是选择用户远程存储上的文件:二是选择本地文件系统中的文件进行渲染.由于渲染任务是在远程主机上进行的,实际进行渲染时源文件也是在ftp目录 ...
- 1个小时学会ReactiveCocoa基本使用
来源:朱凯奇 链接:http://www.jianshu.com/p/5d966074741a 1.ReactiveCocoa简介 ReactiveCocoa(简称为RAC),是由Github开源的一 ...
- Linux下搭建Oracle11g RAC(5)----配置ASM磁盘
将共享磁盘格式化.然后用asmlib将其配置为ASM磁盘,用于将来存放OCR.Voting Disk和数据库用. 注意:只需在其中1个节点上格式化就可以,接下来我们选择在node1节点上格式化. 这里 ...
- JS实例(一)
一:单选按钮,选择同意,提交变为可用,反正提交不可用: HTML里面代码: <form id="f1" name="f1"> <input t ...
- python matplotlib.plot画图显示中文乱码的问题
在matplotlib.plot生成的统计图表中,中文总是无法正常显示.在网上也找了些资料,说是在程序中指定字体文件,不过那样的话需要对plot进行很多设置,而且都是说的设置坐标轴标题为中文,有时候图 ...
- mysql命令使用
1.连接Mysql 格式: mysql -h主机地址 -u用户名 -p用户密码 1.连接到本机上的MYSQL.首先打开DOS窗口,然后进入目录mysql\bin,再键入命令mysql -u root ...
- Java 非对称加密
package test; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.Object ...
- LINQ数据库连接对象制造工厂
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.D ...
- mvc性能优化
mvc性能优化 (1)移动设备卡顿问题 -1请求方式 在mvc中GET请求有问题,出现错误 在MVC中在进行GET请求获取JSON数据时,需要进行如下设置: return Json("&qu ...
- A题笔记(10)
No.1390 代码:https://code.csdn.net/snippets/191965 另一版本:https://code.csdn.net/snippets/192009 考察点有两个: ...