房产售卖信息查询器-React Native实战

来源:互联网 发布:装修报价软件排名 编辑:程序博客网 时间:2024/04/29 15:31

转自:http://1ke.co/course/269

如有侵犯,请来信oiken@qq.com


原作:React Native Tutorial: Building Apps with JavaScript , Colin Eberhardt 2015-3-26 

翻译整理 1ke.co 魔王 文中有改动,以原作为准,不作说明

 

几个月之前,Facebook发布了React Native,用这个框架,我们就可以用Javascript开发原生iOS程序了(安卓版大概几个月后发布)!3月底,Facebook已经放出了官方repository 。

 

我们都在用js和HTML5写App,然后用PhoneGap包好发布到iOS(和安卓)了。 React Native真的好用么?

 

React Native还真就是那么好用。程序猿都为此兴奋无比,主要有以下两个原因:

 

  1. 用了React Native, 你的程序尽管用JavaScript写,UI却是本地的,因此无需承受HTML5的UI带来的用户体验损失。
  2. React 有特别的构建用户界面的技巧,使得界面成为一个叙述软件工作状态的交流窗口。

 

关键是,React Native是要把React的编程方式带到移动端App开发中。和Java的“开发一次,到处使用”的谎言不同,React主张“学习一次,到处开发”,至少比较现实。本教程暂时只讨论iOS(因为安卓版还没出),但是你看完以后,一定也知道在安卓上怎么用了。

 

如果你只用Objective-C或者Swift开发程序,可能你不会对用JavaScript开发程序有想法。但是作为Swift的开发者,多一门手艺并不是坏事。

 

用过Swift的你,一定掌握了更为有效和规范的算法,更好的控制程序的稳定性和可移植性。但实际上,构建UI的方法和Objective-C并没有多大区别:还是在用UIKit而且别无选择。

 

而我们的React引入了虚拟DOM的概念,将功能性(甚至面向对象)直接带到了UI开发中来。

 

本教程中,咱们来做一个搜索英国房产并生成列表的软件。

 

 

如果你没用过JavaScript也没关系,本会会详解代码。React 用CSS属性来管理样式,很简单,当然如果你有需要的话可以专门去学习一下。

 

好,我们开始吧。

起步

React Native框架就在GitHub上,你可以克隆一个项目,也可以下载一个zip文件。如果并不想看源代码,也可以用命令行来创建一个React Native项目。本教程中,咱们就这么做。

 

React使用Node.js,来构建JavaScript代码。如果没有的话需要装上。

 

先安装Homebrew(本机使用的是MAC,应该不用解释了),然后命令行安装Node.js

brew install node

 

再安装 watchman,Facebook的一款文件监控程序。

brew install watchman

 

有了这个React Native就可以监察文件的变化并重建。就好比每次保存都用Xcode来build一次。

 

接下来就可以用npm安装 React Native CLI(命令行工具)。

npm install -g react-native-cli

 

这就用Node的包管理工具在全局安装了CLI。这方面npm有点像CocoaPods 或者Carthage。

 

创建一个项目目录,然后进入这个目录:

react-native init PropertyFinder

 

这就创建了一个叫做PropertyFinder(找房产)的初始项目,里面包含了构建和运行 React Native 程序所需的所有内容。

 

进入这个目录,你会找到一个node_modules文件夹,这里面放着React Native框架。

 

同时还会找到一个 index.ios.js文件,这就是CLI创建的程序文件的骨架。

 

最后,还会看到一个Xcode项目文件和iOS目录,包含了启动程序必须的相关文件。

 

下面我们build和运行一下这个程序:

 

同时我们会注意到一个窗口跳出来:

=============================================================== |  Running packager on port 8081.        |  Keep this packager running while developing on any JS          |  projects. Feel free to close this tab and run your own       |  packager instance if you prefer.                               |                                                               |     https://github.com/facebook/react-native                  |                                                               ===============================================================Looking for JS files in   /Users/colineberhardt/Temp/TestProjectReact packager ready.

 

这是React Native的包管理程序,在Node下面运行,咱们就知道这是干什么的了。

 

不要关掉这个窗口,让它在后台自己运行。如果不小心关掉了,重新运行Xcode项目就行。

 

注意:我们等一下要写大量的JavaScript代码,最好选一个轻量级的编辑器,推荐Sublime Text,当然你自己可以在网上找找。

Hello React Native

在做房产搜索App之前,咱们先做一个Hello World程序。我们在这里会接触到很多重要的组件。

 

在编辑器中打开index.ios.js,删掉全部内容,然后写下这么一行:

'use strict';

 

启用Strict模式可以增强JavaScript的除错能力并关掉一些不好的功能。

 

然后加上下面这行:

var React = require('react-native');

 

这就把React Native组件调用起来,并赋予React变量。其中require功能和Node.js是一样的。

 

接下来,加上下面这些:

var styles = React.StyleSheet.create({  text: {    color: 'black',    backgroundColor: 'white',    fontSize: 30,    margin: 80  }});

 

这段代码添加了文字的样式。如果你干过web开发,你一定知道这就是CSS。

 

好了,下面真的是app程序了。

class PropertyFinderApp extends React.Component {  render() {    return React.createElement(React.Text, {style: styles.text}, "Hello World!");  }}

 

看到了吗?一个JavaScript类(Class)!

 

类从ECMAScript 6 (ES6)被引入JavaScript。由于JavaScript不断在改进,web开发人员经常因为浏览器兼容性(万恶的IE)而不得不放弃一些功能。React Native有一个JavaScriptCore,因此无需为浏览器兼容性操心。

 

注意:如果你是web开发者,我建议先从新的JavaScript标准出发,然后使用Babel之类的工具兼容旧版浏览器。

 

PropertyFinderApp 继承 React.Component,React UI的基础模块。Component(组件)包含了不可变的property(属性变量),可变的state(状态变量),并提供一个render(渲染)方法。

 

React Native 组件不是UIKit类,而是轻量级的。框架会将React组件树转换为本地UI。

 

最后我们加上这行:

React.AppRegistry.registerComponent('PropertyFinder', function() { return PropertyFinderApp });

 

AppRegistry 定义了程序的入口变量,并提供了根组件。

 

保存好index.ios.js,返回Xcode,确认把PropertyFinder 加进iPhone模拟器,我们重建并运行一下项目。

 

 

这可是本地UI,没有任何浏览器,代码全是JavaScript写的哦!

 

还是不信?:)打开Xcode的Debug\View Debugging\Capture View Hierarchy,是不是哪都找不到UIWebView ,只有一个真正的View!牛啊。

 

 

你一定很好奇是怎么回事。在Xcode中找到application:didFinishLaunchingWithOptions:,这个方法创建了一个RCTRootView,来载入JavaScript并渲染了View。

 

程序一运行,RCTRootView 立刻读取:

http://localhost:8081/index.ios.bundle

来加载程序。

 

现在知道前面弹出来那个窗口是干什么的了吧,就是处理这个东西的。

 

程序启动时,代码会载入JavaScriptCore 框架并执行。在这个程序中,PropertyFinderApp 这个component会被加载并创建一个本地的UIKit视图。后面会详细讲到。

Hello JSX

我们现在的程序使用 React.createElement创建,然后React转换为本地文件。尽管目前我们的js文件很清楚,但随着代码更复杂,各种元素的嵌套会导致代码一团乱麻。

 

在app继续运行的情况下,我们回到index.ios.js,把return改一改:

return <React.Text style={styles.text}>Hello World (again)</React.Text>;

 

这是JSX代码,全称叫做JavaScript syntax extension,使用类似HTML的标签。如果是web开发者,一看就懂。我们全程会大量使用JSX代码。

 

保存文件返回模拟器,按下Cmd+R,我们可以看到“Hello World (again)”,看到没,重新加载React Native程序就跟刷新一下网页一样简单。

 

由于我们的程序使用的是同一套JavaScript文件,以后只需要修改index.ios.js并保存,就可以实现内容的调整。

 

好了,玩够了Hello World,我们来做真正的程序吧。

添加导航控制

我们的寻找房产程序使用和UIKit类似的栈形导航。下面来做这个导航。

 

 在index.ios.js中,我们把PropertyFinderApp 重命名为HelloWorld。

class HelloWorld extends React.Component {

 

Hello World还是会显示,不过已经不是咱们的根组件了。

 

我们在下面加一点真货:

class PropertyFinderApp extends React.Component {  render() {    return (      <React.NavigatorIOS        style={styles.container}        initialRoute={{          title: 'Property Finder',          component: HelloWorld,        }}/>    );  }}

 

这样就添加了一个导航控制器,使用了原本HellowWorld组件的路径和样式。在web开发时,routing(路由或路径)被用来设计程序的导航结构,根据URL来分配页面。

 

然后我们要调整样式:

var styles = React.StyleSheet.create({  text: {    color: 'black',    backgroundColor: 'white',    fontSize: 30,    margin: 80  },  container: {    flex: 1  }});

 

回到Xcode再刷新一次。

 

 

好了现在我们有了导航框架,可以开始在里面做UI了。

构建搜索页面

我们创建一个新文件SearchPage.js,和index.ios.js放在一起。添加如下代码:

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

 

前面就不说了,下面那个变量声明你可能没有见过。

 

这叫做destructuring assignment(解构化赋值),可以把对象的多个属性赋给相应变量。于是,之后你就不必使用React这个前缀,比如你可以直接调用StyleSheet 而不是React.StyleSheet。它还可以用来操作数组,非常好用。

 

我们还要添加样式:

var styles = StyleSheet.create({  description: {    marginBottom: 20,    fontSize: 18,    textAlign: 'center',    color: '#656565'  },  container: {    padding: 30,    marginTop: 65,    alignItems: 'center'  }});

 

虽然用css没有Interface Builder那么可视化,但比用viewDidLoad()一个一个设置视图属性可方便多了。:)

 

下面来写组件:

class SearchPage extends Component {  render() {    return (      <View style={styles.container}>        <Text style={styles.description}>          Search for houses to buy!        </Text>        <Text style={styles.description}>          Search by place-name, postcode or search near your location.        </Text>      </View>    );  }}

 

render 可以完美应用JSX的结构。用css我们很容易就实现了这个图形界面:一个容器和两条label。

最后加上:

module.exports = SearchPage;

 

这条命令制定了输出SearchPage 以为他用。

 

下一步就是路由(Routing)的制作。

 

打开index.ios.js,并且在require下面加上这行:

var SearchPage = require('./SearchPage');

 

PropertyFinderApp 的render里面替换initialRoute :

component: SearchPage

 

现在可以干掉Hello World这种新手用的东西了,现在我们是高手了。

 

回模拟器我们刷新一下Cmd + R:

 

 

这就是我们的新组件,搜索页SearchPage

用Flexbox来布局

现在,我们用css调整了margin、padding和颜色,不过你可能对flexbox并不熟悉,这是最近出现的新规范,用来做app的UI布局再好不过了。

 

React Native用的是css-layout库,是flexbox标准的一种javascript实现,可以转化为C(iOS)和Java(安卓)。

 

在App中,默认的排列是依据column(列),所以,子元素默认是垂直排列的。

 

 

这个被叫做主轴(main axis),可以竖着也可以横着。

 

每个子元素的垂直位置由margin、height和padding决定。容器同时将alignItems 设置为center,所以文档是居中的。

 

好了,现在要加上input和button了。打开SearchPage.js并将下面这段插入到第二段text元素后面:

<View style={styles.flowRight}>  <TextInput    style={styles.searchInput}    placeholder='Search via name or postcode'/>  <TouchableHighlight style={styles.button}      underlayColor='#99d9f4'>    <Text style={styles.buttonText}>Go</Text>  </TouchableHighlight></View>
<TouchableHighlight style={styles.button}    underlayColor='#99d9f4'>  <Text style={styles.buttonText}>Location</Text></TouchableHighlight>

 

现在我们有两个顶级视图了,一个有一个输入框和按钮,一个只有按钮。

 

下面我们把样式加进style里面:

flowRight: {  flexDirection: 'row',  alignItems: 'center',  alignSelf: 'stretch'},buttonText: {  fontSize: 18,  color: 'white',  alignSelf: 'center'},button: {  height: 36,  flex: 1,  flexDirection: 'row',  backgroundColor: '#48BBEC',  borderColor: '#48BBEC',  borderWidth: 1,  borderRadius: 8,  marginBottom: 10,  alignSelf: 'stretch',  justifyContent: 'center'},searchInput: {  height: 36,  padding: 4,  marginRight: 5,  flex: 4,  fontSize: 18,  borderWidth: 1,  borderColor: '#48BBEC',  borderRadius: 8,  color: '#48BBEC'}

 

每项属性中用“,”隔开。这些就是输入框和按钮的样式。

 

我们会模拟器刷新一下 Cmd + R。

 

 

输入框和Go按钮在同一行,因为把它们放在了flexDirection: 'row'的容器中。我们并没有明确声明它们的宽度,而是给了一个flex值。输入框flex: 4,而按钮则是 flex: 1,所以它们的宽度比为4:1.

 

你可能已经注意到,这个按钮……并不是按钮。在UIKit中,按钮只不过是可以点击的Label,所以React Native团队决定直接用JavaScript构建按钮。程序中的按钮使用TouchableHighlight这个组件,使得按钮按下时显示透明和不同的颜色。

 

最后我们添上一些图片。看下面的图。在Xcode中,打开 Images.xcassets,点击加号添加图片。接下来鼠标拖动图片到相应的格子。

 

 

这次需要停掉程序并重启,来让图片加载出现。

 

将下面这行添加到location按钮TouchableHighlight 组件的下面:

<Image source={require('image!house')} style={styles.image}/>

 

下面添加图片的样式:

image: {  width: 217,  height: 138}

 

上面的require('image!house') 是为了获取图片的位置。在Xcode中,如果打开Images.xcassets的话,我们会找到房屋图标。

 

返回模拟器刷新一下页面,Cmd + R。

 

注意:如果看不到图片而显示“image!house”的话,重启包管理器(或者命令行npm start)

添加组件state(状态参数)

每一个React组件都有其state状态参数,用来储存关键变量。组件初始化之前要先初始化state。

SearchPage.js中,在render()上面添加如下代码:

constructor(props) {  super(props);  this.state = {    searchString: 'london'  };}

 

我们的组件现在有了一个state变量,名为searchString值为’london’。

 

然后就可以使用组件的状态了。在Render中,改变TextInput如下代码:

<TextInput  style={styles.searchInput}  value={this.state.searchString}  placeholder='Search via name or postcode'/>

 

我们把输入框的值——也就是用户看到的那个——和我们的state值searchString 绑定起来了。

好了,值是初始化好了,但是,当用户输入时,会发生什么呢?(自己去试试)

 

我们要绑定一个事件,在SearchPage中添加如下方法:

onSearchTextChanged(event) {  console.log('onSearchTextChanged');  this.setState({ searchString: event.nativeEvent.text });  console.log(this.state.searchString);}

 

这样,系统就会取出输入框的值并且替换state的值。

 

下面要把这个方法串进输入框中,要将函数绑定到onChange属性上:

<TextInput  style={styles.searchInput}  value={this.state.searchString}  onChange={this.onSearchTextChanged.bind(this)}  placeholder='Search via name or postcode'/>

 

这样每次用户在输入框中输入,都会触动onChange,并执行onSearchTextChanged

 

注意:如果不明白bind(this)方法是干什么的,JavaScript处理this关键字与其他语言不太一样,用Swift翻译就是self。用bind是为了确保this指向组件实例。可以自己去试试就明白了。

 

最后,把下面这些记录函数添加到render中,return上面:

console.log('SearchPage.render');

 

这样我们就会明白一些非常神奇的东西。

 

现在刷新一下程序。我们应该可以看到界面中的”london”,并且在命令行中,我们看到:

 

 

嗯……顺序似乎有点问题。

  1. 首次启动render
  2. 当输入框有变化,启动onSearchTextChanged()
  3. 更新state,并引发另一次render
  4. onSearchTextChanged()并装入新的搜索内容

 

每次任何state发生变化,React组件会将整个UI全部重新render一遍,这是好的,这就解耦了state与UI状态。

 

在其他的UI框架中,要么你手动更改UI的内容,要么进行双向绑定,比如某些MVVM模式的框架。

 

在React中,你无需担心哪块UI绑到什么地方,整个UI都是state的一个体现。

 

看到这里,你可能已经发现这个模式的缺点了——性能问题!

 

我们总不能每次state有点改动,就把整个UI全部扔掉,然后重新加载吧?好了React在这个问题上相当智能化。每次UI渲染时,都会把render方法返回的视图树,拿去与现存的UIKit视图树作对比。于是对比的结果,就是需要更改的视图树。所以只有改变了的部分需要重新渲染。

 

看到这里我是非常佩服React.js的虚拟DOM给iOS带来的如此特别的合作方式。

 

我们把上面那些日志方法全部删掉,下面我们开始完善程序。

启动搜索功能

要实现搜索功能,我们要处理Go按钮的按下事件,并发出API请求,同时视觉上告诉用户请求正在进行。

 

在 SearchPage.js中,更改初始化state:

this.state = {  searchString: 'london',  isLoading: false};

 

在render中增加如下内容:

var spinner = this.state.isLoading ?  ( <ActivityIndicatorIOS      hidden='true'      size='large'/> ) :  ( <View/>);

 

根据isLoading这个state是否为真,显示活动视图或空视图。

 

在搜索UI的return中,在Image下面,增加:

{spinner}

 

把下面这个属性加到Go按钮的渲染方法中的TouchableHighlight 标签里:

onPress={this.onSearchPressed.bind(this)}

 

 

然后在SearchPage 类中加入以下方法:

_executeQuery(query) {  console.log(query);  this.setState({ isLoading: true });}onSearchPressed() {  var query = urlForQueryAndPage('place_name', this.state.searchString, 1);  this._executeQuery(query);}

 

现在的_executeQuery只是写一条日志,将来会完善的。

 

注意: JavaScript的类没有私有前缀,所以前面加个_来表示私有。

 

当Go按钮按下时,程序会执行onSearchPressed(),启动查询。

 

最后,在searchPage类的声明上加上这个工具函数:

function urlForQueryAndPage(key, value, pageNumber) {  var data = {      country: 'uk',      pretty: '1',      encoding: 'json',      listing_type: 'buy',      action: 'search_listings',      page: pageNumber  };  data[key] = value;  var querystring = Object.keys(data)    .map(key => key + '=' + encodeURIComponent(data[key]))    .join('&');  return 'http://api.nestoria.co.uk/api?' + querystring;};

 

这个函数和searchPage没有依赖,所以作为一个独立函数,而不作为类的方法。这个函数先用一个data来储存参数,然后构建查询请求。其中,=>这是个新语法,(参数)=>{函数体}。

 

回到模拟器,按下Cmd + R刷新,点击Go,就会看到滚动图标,然后我们看看后台。

 

 

我们会看到url,复制下来贴进浏览器,我们来看看结果,会看到一大段JSON内容。现在还不用管这些。

 

注意:本教程使用了 Nestoria API 来获取房产信息。相关细节可以参考网站文档。

 

下一步,就是在App内发起请求了。

发起API请求

还是在SearchPage.js,添加一个message变量。

 

this.state = {  searchString: 'london',  isLoading: false,  message: ''};

 

在render中,在底部添加一行:

<Text style={styles.description}>{this.state.message}</Text>

 

这个可以用来显示信息给用户看。

 

SearchPage 中,添加以下代码到_executeQuery()中:

fetch(query)  .then(response => response.json())  .then(json => this._handleResponse(json.response))  .catch(error =>      this.setState({      isLoading: false,      message: 'Something bad happened ' + error   }));

 

这里用到了fetch方法,来自Web API,提供了一个比XMLHttpRequest更好的API接口。异步回应返回一个promise,带来目标路径以及JSON文档。

 

最后我们来处理返回的文档,把这段加在SearchPage里面:

_handleResponse(response) {  this.setState({ isLoading: false , message: '' });  if (response.application_response_code.substr(0, 1) === '1') {    console.log('Properties found: ' + response.listings.length);  } else {    this.setState({ message: 'Location not recognized; please try again.'});  }}

 

拿掉了isLoading并且做了日志记录。

 

保存起来,然后回模拟器Cmd + R。我们搜索london,应该会看到20条记录在日志里面。然后试一下随便输一个’ narnia’,你会看到如下情况:

 

显示结果

创建一个SearchResults.js,并添加代码如下:

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

 

这一段,看是很眼熟吧?

 

然后是组件本身:

class SearchResults extends Component {   constructor(props) {    super(props);    var dataSource = new ListView.DataSource(      {rowHasChanged: (r1, r2) => r1.guid !== r2.guid});    this.state = {      dataSource: dataSource.cloneWithRows(this.props.listings)    };  }   renderRow(rowData, sectionID, rowID) {    return (      <TouchableHighlight          underlayColor='#dddddd'>        <View>          <Text>{rowData.title}</Text>        </View>      </TouchableHighlight>    );  }   render() {    return (      <ListView        dataSource={this.state.dataSource}        renderRow={this.renderRow.bind(this)}/>    );  } }

 

上面的代码用了一个ListView 组件,用于在可滚动的容器内添加多行内容,有点像UITableView。数据的来源是ListView.DataSource

 

构建数据源时,需要提供一个函数来对比一对数据。ListView在组建的过程中会调用这个函数,用于编制列表。这次的回传数据中,Nestoria API提供guid ,就刚好适合这个目的。

 

别忘了添加输出模块代码:

module.exports = SearchResults;

 

讲一下这段添加到SearchPage.js中,require下面:

var SearchResults = require('./SearchResults');

 

这样我们就可以在SearchPage  中调用SearchResults 了。

 

编辑_handleResponse 方法:

this.props.navigator.push({  title: 'Results',  component: SearchResults,  passProps: {listings: response.listings}});

 

上面的代码导向SearchResults 页面,并且将结果列表传进去。用push方法将搜索结果传入导航,这样就可以用回退功能返回之前页面。

 

返回模拟器并Cmd + R,你会看到列表:

 

 

当然,这是看不得的,太丑了。

妙手生花

React Native 对你来说应该比较熟了,我们就加快脚步。

 

将代码加到SearchResults.js:中的结构化定义下面:

var styles = StyleSheet.create({  thumb: {    width: 80,    height: 80,    marginRight: 10  },  textContainer: {    flex: 1  },  separator: {    height: 1,    backgroundColor: '#dddddd'  },  price: {    fontSize: 25,    fontWeight: 'bold',    color: '#48BBEC'  },  title: {    fontSize: 20,    color: '#656565'  },  rowContainer: {    flexDirection: 'row',    padding: 10  }});

 

然后把renderRow()替换成下面这些

renderRow(rowData, sectionID, rowID) {  var price = rowData.price_formatted.split(' ')[0];  return (    <TouchableHighlight onPress={() => this.rowPressed(rowData.guid)}        underlayColor='#dddddd'>      <View>        <View style={styles.rowContainer}>          <Image style={styles.thumb} source={{ uri: rowData.img_url }} />          <View  style={styles.textContainer}>            <Text style={styles.price}>£{price}</Text>            <Text style={styles.title}                   numberOfLines={1}>{rowData.title}</Text>          </View>        </View>        <View style={styles.separator}/>      </View>    </TouchableHighlight>  );}

这次,图片使用URL,React Native会负责从主线程中解析。而且rowData.grid使用=>方法来获取。

 

最后一步是处理按下:

rowPressed(propertyGuid) {  var property = this.props.listings.filter(prop => prop.guid === propertyGuid)[0];}

 

刷新一下,我们可以看到:

 

看起来很不错。但是,这个房价根本没人买的起吧。

 

我们准备将最后一个视图做出来。

房产详情视图

新建一个PropertyView.js,然后添加以下代码:

'use strict';var React = require('react-native');var {  StyleSheet,  Image,   View,  Text,  Component} = React;

 

现在,你睡着了都应该写的出来。

 

然后是样式:

var styles = StyleSheet.create({  container: {    marginTop: 65  },  heading: {    backgroundColor: '#F8F8F8',  },  separator: {    height: 1,    backgroundColor: '#DDDDDD'  },  image: {    width: 400,    height: 300  },  price: {    fontSize: 25,    fontWeight: 'bold',    margin: 5,    color: '#48BBEC'  },  title: {    fontSize: 20,    margin: 5,    color: '#656565'  },  description: {    fontSize: 18,    margin: 5,    color: '#656565'  }});

 

接着是组件:

class PropertyView extends Component {  render() {    var property = this.props.property;    var stats = property.bedroom_number + ' bed ' + property.property_type;    if (property.bathroom_number) {      stats += ', ' + property.bathroom_number + ' ' + (property.bathroom_number > 1        ? 'bathrooms' : 'bathroom');    }    var price = property.price_formatted.split(' ')[0];    return (      <View style={styles.container}>        <Image style={styles.image}             source={{uri: property.img_url}} />        <View style={styles.heading}>          <Text style={styles.price}>£{price}</Text>          <Text style={styles.title}>{property.title}</Text>          <View style={styles.separator}/>        </View>        <Text style={styles.description}>{stats}</Text>        <Text style={styles.description}>{property.summary}</Text>      </View>    );  }}

 

render的第一部分对数据做了一些处理,因为很多情况下数据是不完整的。

 

最后别忘了输出:

module.exports = PropertyView;

 

回到SearchResults.js,增加一个require,你懂的:

var PropertyView = require('./PropertyView');

 

改一下rowPressed()来实现PropertyView的导航。

rowPressed(propertyGuid) {  var property = this.props.listings.filter(prop => prop.guid === propertyGuid)[0];  this.props.navigator.push({    title: "Property",    component: PropertyView,    passProps: {property: property}  });}

 

刷新一下,你也懂的。

 

最后一步:让用户搜索附近的房子。

基于地理位置的搜索

在Xcode中,打开 Info.plist并添加新key,右键点击编辑期内并选择Add RowNSLocationWhenInUseUsageDescription 作为键名,以下作为键值:

PropertyFinder would like to use your location to find nearby properties

 

以下是完成的plist :

 

打开SearchPage.js,找到渲染“Location”那个按钮的TouchableHighlight,并且加上下列:.

onPress={this.onLocationPressed.bind(this)}

 

当你点击按钮,就会触发onLocationPressed 。

 

打开SearchPage ,添加:

onLocationPressed() {  navigator.geolocation.getCurrentPosition(    location => {      var search = location.coords.latitude + ',' + location.coords.longitude;      this.setState({ searchString: search });      var query = urlForQueryAndPage('centre_point', search, 1);      this._executeQuery(query);    },    error => {      this.setState({        message: 'There was a problem with obtaining your location: ' + error      });    });}

 

获取现在位置的方法是navigator.geolocation,这是Web API,React有自己的一套实现,用的是iOS原生的位置服务。

 

如果成功获取现在的位置,系统会向Nestoria拉取数据,如果失败,就会显示错误信息。

 

由于改变了plist,这次我们就不能Cmd +R了。重新建立工程并启动吧。

 

在启动位置搜索服务前,我们必须指定一处Nestoria 数据库覆盖的地方。作为测试,我们在模拟器中选择Debug\Location\Custom Location输入北纬55.02,经度-1.42,这是北英格兰一处安静的港口。

 

今后的路

恭喜你,做出了第一个React Native程序!源代码可以在Github上找到!

 

如果你是web开发的世界过来的,你会发现用javaScript和React做一个本地iOS(几个月后安卓)App是多么简单的事情。

 

也许你下一个App就用这套程序写了?还是说你会坚持用Objective-C或者Swift?不论如何,还是希望本篇文章让你有所收获,并且在下一个项目中有新的理念。

 

如果有任何疑问,欢迎到1ke.co上讨论,或者到原作者Colin Eberhardt论坛提问。本文完成翻译时(2015年6月13日),Andoid版的发布日期暂定在2015年10月。我们拭目以待。


0 0
原创粉丝点击