編者注:我們發現了比較有趣的系列文章 《30天學習30種新技術》
,準備翻譯,一天一篇更新,年終禮包。下面是第10天的内容。
今天又是
“30天學習30種新技術” 的一天。長期以來,我覺得手機開發很恐怖,大部分應用都沒有商業模式。事實上,編寫手機應用從來都不能讓我興奮。然而,考慮到手機領域的飛速發展,以及更多的人通過手機而不是桌面通路網際網路,我決定嘗試下手機開發。我的手機開發之旅将從 PhoneGap起步。
本文首先介紹 PhoneGap 的基本情況,接着我們使用 PhoneGap 開發一個手機應用。

手機應用
我們将為是
開發一個手機閱讀器。使用者可以将該應用安裝在Android、Symbian、webOS或Windows裝置上。該手機應用可以在
https://build.phonegap.com/apps/635001/share下載下傳。
這個應用可以做到:
給出所有已經釋出的系列文章的清單。使用者點選清單中的條目則調用浏覽器打開連結。
讀者可以使用該應用回饋想法。
什麼是 PhoneGap?
是一個自由開源的手機開發架構,可以使用标準的 web 技術,即 HTML,CSS 和 JavaScript 進行手機應用開發。
它将應用的web資源封裝在原生應用中,這些應用可以送出到各大應用商店。更重要的是,我們可以通過 PhoneGap 進行跨平台的手機應用開發。這意味着,在理想情況下,我們隻需編寫一次手機應用,就可以将該應用移植到多個平台。例如,我為Android裝置編寫了這個應用,然而使用
PhoneGap build,我同時為其他裝置打包了應用。大多數标準的裝置特性,例如照相、地理位置、存儲等都由JavaScript API提供了。取決與目标裝置,PhoneGap提供的JavaScript API會有所不同。
關于 PhoneGap 的一些事實:
- 2009年,Nitobi開發了PhoneGap架構。
- 2011年10月,Adobe 購買了 Nitobi(PhoneGap)背後的公司。
- Adobe 将 PhoneGap 貢獻給 Apache 基金會。
- 此開源項目被命名為 Apache Cordova.
- PhoneGap 是 Adobe 完成的 開源項目 Apache Cordova 的實作。 PhoneGap的核心使用 Apache Cordova.
- PhoneGap使用基于插件的架構。所有裝置的特性以插件方式提供。本文中,我們将使用一些插件。
為什麼我關心PhoneGap?
- 沒有必要為每個平台學習一種原生的開發環境。如果開發者打算針對多個平台,那麼PhoneGap的跨平台特性可以節約開發者大量的時間和精力。我了解HTML、CSS和JavaScript,可以很平滑地進入手機世界。
- 對于開發基于REST API的CRUD手機應用,PhoneGap很好用。
- 它不強迫開發者選擇特定的CSS庫。開發者可以使用任何他們喜歡的移動端的庫。本應用中我使用jQuery mobile。
PhoneGap Prerequisites
PhoneGap依賴NodeJS,我們需要使用npm來安裝PhoneGap。Npm是NodeJS的包管理器,新版的NodeJS自帶npm。你可以從
官網下載下傳最新版的NodeJS。
你需要同時安裝目标機器的SDK。例如,如果我們建立Android程式,我們需要在作業系統上安裝 Android 開發者工具。PhoneGap 使用 SDK 為目标平台建構包。
PhoneGap起步
使用如下指令安裝PhoneGap:
sudo npm install -g phonegap
以上指令将全局安裝 phonegap 包,這樣任何目錄下都可以使用 phonegap。
安裝插件需要安裝Cordova.
使用如下指令安裝Cordova。
sudo npm install -g cordova
GitHub 倉庫
今天的demo應用的代碼在
github上。
建立PhoneGap應用
phonegap
指令行提供了使用模闆建立新phonegap項目的指令:
phonegap create reader --id io.reader --name Reader30
上面的指令會建立一個
reader
目錄。
io.reader
和
Reader30
是可選的:
io.reader
提供了一個類似反域名的識别符,而
Reader30
提供了應用的顯示文本。
應用的檔案結構如下:
讓我們看下這些生成的目錄:
-
檔案夾存放專門針對某個特定平台的資源。例如,我們可能使用merges
來為Android裝置改變字型大小。merges
-
目錄存放平台建構檔案。platforms
-
存放應用使用的插件。當我們安裝一個插件的時候,插件會被存放在這個目錄下。plugins
-
目錄存放應用資源,例如www
、html
css
js
。這是我們要花最多時間的目錄。img
包含了生成和分發應用所需的元資訊。元資訊也包括應用名稱、描述、作者詳情、應用權限等資訊。在 這裡 可以看到一個完整的清單。config.xml
運作下列指令即可在android上運作應用:
phonegap run android
這是我們的android平台閱讀器應用的第一次建構。如果有真實裝置連接配接,應用會跑在真實裝置上。如果沒有裝置,會啟動Android虛拟器,應用會被部署到虛拟器上。
注意:Android虛拟器的性能相當差勁,是以我建議你總是連接配接上你的真實手機裝置。Grant Shipley的
部落格提供了一些加速Android虛拟器的資訊。
開發手機應用
如上所述,我們的應用有兩個頁面。我們先開發第一個。
部落格清單
初始頁面列出所有已釋出文章的清單。我們修改下
index.html
,請從我的
github倉庫中複制css和javascript資源。
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<title>Learn 30 technologies in 30 days</title>
<link rel="stylesheet" href="css/vendor/jquery.mobile-1.3.1.min.css">
<link rel="stylesheet" href="css/vendor/jquery.loadmask.css">
</head>
<body>
<div data-role="page" id="mainPage">
<div data-role="header" data-position="fixed">
<h1>30Technologies30Days</h1>
<a href="#feeback" data-icon="edit" data-theme="b" class="feedback ui-btn-right" data-role="button" data-inline="true" data-ajax="false">Feedback</a>
<div data-role="navbar">
<ul>
<li><a href="#home" class="home ui-btn-active" data-icon="home">Home</a></li>
</ul>
</div>
<div id="main" data-role="content">
<div data-theme="a" data-role="footer">
<h3>
© Shekhar Gulati 2013
</h3>
</div>
</div>
<script type="text/x-mustache-template" id="home-template">
<ul id="blogs" data-role="listview" data-filter="true" data-filter-placeholder="Search blogs..." data-inset="true">
</ul>
</script>
<script type="text/x-mustache-template" id="blog-template">
<li>
<a href="{{url}}" target="_blank">
<h3>{{title}}</h3>
<p><strong>{{publishedOn}}</strong></p>
</li>
<script src="phonegap.js"></script>
<script src="js/vendor/jquery-1.9.1.min.js"></script>
<script src="js/vendor/jquery.mobile-1.3.1.min.js"></script>
<script src="js/vendor/jquery.ui.map.js"></script>
<script src="js/vendor/jquery.loadmask.min.js"></script>
<script src="js/vendor/jquery.timeago.js"></script>
<script type="text/javascript" src="js/vendor/mustache.js"></script>
<script type="text/javascript" src="js/app.js"></script>
</body>
</html>
上面的html導入了所需的css和javascript檔案。它使用jQuery mobile達到原生效果。我們同時定義了一個mustache模闆來呈現清單。
應用相關的javascript代碼放在
app.js
檔案中:
$(document).ready(function(){
homeView();
$('.home').on('tap', renderHomeView);
$('.feedback').on('tap', renderFeedbackFormView);
});
function renderHomeView(event){
event.preventDefault();
}
function homeView(){
$('#main').empty();
$('.home').addClass("ui-btn-active");
$('#main').html(template("home"));
var url = 'http://30technologiesin30days-t20.rhcloud.com/api/v1/blogs';
$.mobile.loading( 'show',{});
$.ajax({
url : url,
dataType : 'json',
success : function(data){
$.mobile.loading( 'hide',{});
$.each(data , function(i , obj){
var template = $("#blog-template").html();
obj.publishedOn = $.timeago(obj.publishedOn);
$("#blogs").append(Mustache.to_html(template,obj));
$('#blogs').listview('refresh');
});
},
error : function(XMLHttpRequest,textStatus, errorThrown) {
$.mobile.loading( 'hide',{text:"Fetching blogs.."});
alert("Something wrong happended on the server. Try again..");
}
})
$('#main').trigger('create');
function template(name) {
return Mustache.compile($('#'+name+'-template').html());
function showNotification(message , title){
if (navigator.notification) {
navigator.notification.alert(message, null, title, 'OK');
} else {
alert(title ? (title + ": " + message) : message);
上面的
app.js
做了這些事:
- 綁定了文檔的就緒事件。一旦就緒,将渲染初始頁面。
- 初始頁面進行REST調用以擷取所有部落格。我們使用jQuery來調用REST。
- 接受資訊後,我們建立一個清單。最後重新整理下。
允許通路REST服務
PhoneGap預設不允許應用通路遠端資源。這就意味着應用無法進行REST調用。為了允許應用進行REST調用,我們需要允許它通路。我們可以通過通配符
*
允許應用通路一切資源。請參閱
文檔了解更多資訊。
在
config.xml
下修改通路控制資訊:
<access origin="*" />
安裝插件
應用使用一些插件來通路特定裝置的特性。
cordova plugin add org.apache.cordova.geolocation
cordova plugin add org.apache.cordova.dialogs
- 第一個指令安裝了
插件。Geolocation提供了裝置位置的資訊,例如經度和緯度。我們稍後将使用此特性。參閱geolocation
- 第二個指令安裝了
插件。dialogs插件提供了原生的裝置視覺提醒。參閱dialogs
回報送出表單
第二個螢幕允許使用者送出回報。
我們增加了一個回報表單,來記錄使用者的回報。
<script type="text/x-mustache-template" id="feedback-form-template">
<form action="" id="feedbackForm">
<div data-role="fieldcontain">
<label for="name">
Describe
</label>
<input type="text" name="name" id="name" placeholder="Full Name eg. Shekhar Gulati ">
</div>
<label for="description">
<textarea name="description" id="description" placeholder="Message for author.."></textarea>
<div id="checkboxes1" data-role="fieldcontain">
<fieldset data-role="controlgroup" data-type="vertical">
<legend>
Share my location
</legend>
<input id="sharemylocation" name="sharemylocation" type="checkbox" value="true">
<label for="sharemylocation">
Share
</label>
</fieldset>
<button id="create-button" data-inline="true">Feedback</button>
</form>
修改
app.js
檔案,監聽tap事件。
function renderFeedbackFormView(event){
$('#main').html(template("feedback-form"));
$('#create-button').bind('tap',shareFeedback);
function shareFeedback(event){
event.preventDefault();
$('#feedbackForm').mask();
var name = $('#name').val();
var description = $('textarea#description').val();
var sharemylocation = $("#sharemylocation:checked").val() === undefined ? "false" : "true";
var data = {name:name , description:description , lngLat :[]};
if(sharemylocation === "true"){
navigator.geolocation.getCurrentPosition(function(position){
var lngLat = [position.coords.longitude , position.coords.latitude];
data.lngLat = lngLat;
postFeedback(data);
} , function(error){
alert('code: ' + error.code + '\n' +
'message: ' + error.message + '\n');
$('#feedbackForm').unmask();
}else{
postFeedback(data);
function postFeedback(data){
type : 'POST',
url : 'http://30technologiesin30days-t20.rhcloud.com/api/v1/feedback',
crossDomain : true,
data : JSON.stringify(data),
dataType : 'json',
contentType: "application/json",
success : function(data){
$('#feedbackForm').unmask();
$('#feedbackForm')[0].reset();
showNotification('Received your feedback', 'Info');
homeView();
},
error : function(XMLHttpRequest,textStatus, errorThrown) {
$('#feedbackForm').unmask();
alert("Error status :"+textStatus);
alert("Error type :"+errorThrown);
}
});
當回報表單送出後,我們從表單擷取了資料。如果使用者勾選了“分享我的位置”,我們會使用geolocation插件來擷取使用者的位置。最後,我們将POST請求送出到我們的REST服務。一旦成功送出,我們顯示一個提醒。
運作應用
現在我們可以在裝置上安裝和運作應用了。使用如下指令: