前两部分,《如何用 React Native 建立一个iOS APP?》,《如何用 React Native 建立一个iOS APP (二)?》中,咱们分别讲了用 React Native 来建立 Navigation Bar,Tab Bar 等这些控件,今天在第三节,咱们着重讲一下剩下的一些控件。闲话少叙,咱们直入主题!html
#####添加一个ListViewreact
React Native 有一个叫作 ListView 的组件,能够显示滚动的行数据,基本上是 ios 项目上的一个术语表视图。 首先,按照所显示的修改解构的声明以包含多个组件,而后就可使用。ios
var { Image, StyleSheet, Text, View, Component, ListView, TouchableHighlight } = React;
添加如下风格样式表:git
separator: { height: 1, backgroundColor: '#dddddd' }
添加如下BookList类构造函数:github
constructor(props) { super(props); this.state = { dataSource: new ListView.DataSource({ rowHasChanged: (row1, row2) => row1 !== row2 }) }; }
而后添加如下功能:json
componentDidMount() { var books = FAKE_BOOK_DATA; this.setState({ dataSource: this.state.dataSource.cloneWithRows(books) }); }
在构造函数中,咱们建立一个列表视图。数据源对象,并将其分配给数据源属性。列表视图使用的数据源是一个接口,能够肯定更新了的 UI 改变所在的行。咱们提供一个函数来比较双行的同一性,它能够用来决定数据列表的改变。react-native
当组件加载/安装到用户界面视图时 componentDidMount() 便被调用。当这个函数被调用时,咱们能够从咱们的数据对象中设置数据源属性。 修改 render() 函数以下图所示:api
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 官方博客