文档章节

如何用 React Native 创建一个iOS APP?(三)

OneAPM蓝海讯通
 OneAPM蓝海讯通
发布于 2016/01/18 15:46
字数 2969
阅读 58
收藏 1
ios

前两部分,《如何用 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:

  1. Native的原生控件有更好的体验;

  2. Native有更好的手势识别;

  3. Native有更合适的线程模型,尽管Web Worker可以解决一部分问题,但如图像解码、文本渲染仍无法多线程渲染,这影响了 Web 的流畅性。

“学习一次,写的任何地方”。仅这一点就可能使其值得学习如何使用框架。

想要了解关于 React Native 更多的内容,你可以看下面的视频,也可以参考文档。

  1. Introducing React Native.

  2. Deep Dive into React Native.

  3. React Native and Relay: Bringing Modern Web Techniques to Mobile.

以上这些仅供参考,你可以在[这里](https://github.com/appcoda/React-Native-Demo-App)下载 Xcode 项目。

OneAPM Mobile Insight ,监控网络请求及网络错误,提升用户留存。访问 OneAPM 官方网站感受更多应用性能优化体验,想阅读更多技术文章,请访问 OneAPM 官方技术博客 本文转自 OneAPM 官方博客

© 著作权归作者所有

共有 人打赏支持
OneAPM蓝海讯通
粉丝 93
博文 631
码字总数 1266889
作品 0
海淀
私信 提问
从Android到React Native开发(四、打包流程解析和发布为Maven库)

1、从Android到React Native开发(一、入门) 2、从Android到React Native开发(二、通信与模块实现) 3、从Android到React Native开发(三、自定义原生控件支持)  作为失踪人口,本篇是对...

恋猫月亮
2018/06/13
0
0
React Native入门:环境安装和在模拟机运行

备注:因为我都是在mac平台上进行开发,所以这里的环境安装是也是针对于mac平台的,下方命令的brew在linux平台上是apt,还需要找对应linux平台对应的命令,这点请注意。 学习rn的前提: 熟悉...

藤原佐为
2017/09/29
0
0
如何用 React Native 创建一个iOS APP?

诚然,React Native 结合了 Web 应用和 Native 应用的优势,可以使用 JavaScript 来开发 iOS 和 Android 原生应用。在 JavaScript 中用 React 抽象操作系统原生的 UI 组件,代替 DOM 元素来渲...

OneAPM蓝海讯通
2016/01/06
65
0
【React Native开发】React Native库版本升级(Upgrading)与降级讲解

(一)前言 现在React Native For Android刚 刚开源才不久,现在整体功能还不健全完善以及开发中的坑(Bug)还是比较多的 ,以至于到现在还没有正式版本。项目人员正在加紧开发维护,修复Bug,...

freecsdn
2016/09/12
36
0
[react-native学习记录]创建/运行android项目

一、 创建项目命令 npm install -g react-native-cli 说明:react-native-cli是一个终端命令行工具,-g表示全局模式。可以完成其余的设置工作。它可以通过npm安装。刚才这条命令会往你的终端...

张小贤
2016/03/03
84
0

没有更多内容

加载失败,请刷新页面

加载更多

StringRedisTemplate的使用

《如何使用RedisTemplate访问Redis数据结构》

yan_liu
30分钟前
2
0
好程序员大数据教程Hadoop全分布安装(非HA)

  机器名称 启动服务   linux11 namenode secondrynamenode datanode   linux12 datanode   linux13 datanode   第一步:更改主机名,临时修改+永久修改   临时修改:hostname ...

好程序员IT
33分钟前
0
0
案例:通过shell脚本实现mysql数据备份与清理

Shell是系统的用户界面,提供了用户与内核进行交互操作的一种接口。它接收用户输入的命令并把它送入内核去执行,实际上Shell是一个命令解释器,它解释由用户输入的命令并且把它们送到内核,不仅...

Linux就该这么学
38分钟前
4
0
springmvc源码解析之@EnableWebMvc五

说在前面 本次主要介绍springmvc配置解析。关注“天河聊架构”微信公众号有更多精彩。 springmvc配置解析 @EnableWebMvc这个注解干了什么,初始化HandlerExceptionResolver 进入到这个方法o...

天河2018
39分钟前
1
0
开源 java CMS - FreeCMS2.8 数据对象 user

项目地址:http://www.freeteam.cn/ user 在使用用户相关标签时,标签会封装user供页面调用。 属性 说明 id id ismail 是否接收互动信件 name 名称 unitNames 所属单位名称,多个之间用;号分...

freeteam
48分钟前
2
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部