天天看點

Day 10: PhoneGap —— 開發手機應用如此簡單

編者注:我們發現了比較有趣的系列文章 《30天學習30種新技術》

,準備翻譯,一天一篇更新,年終禮包。下面是第10天的内容。

今天又是

“30天學習30種新技術” 的一天。長期以來,我覺得手機開發很恐怖,大部分應用都沒有商業模式。事實上,編寫手機應用從來都不能讓我興奮。然而,考慮到手機領域的飛速發展,以及更多的人通過手機而不是桌面通路網際網路,我決定嘗試下手機開發。我的手機開發之旅将從 PhoneGap

起步。

本文首先介紹 PhoneGap 的基本情況,接着我們使用 PhoneGap 開發一個手機應用。

Day 10: PhoneGap —— 開發手機應用如此簡單

手機應用

我們将為是

開發一個手機閱讀器。使用者可以将該應用安裝在Android、Symbian、webOS或Windows裝置上。該手機應用可以在

https://build.phonegap.com/apps/635001/share

下載下傳。

這個應用可以做到:

給出所有已經釋出的系列文章的清單。使用者點選清單中的條目則調用浏覽器打開連結。

Day 10: PhoneGap —— 開發手機應用如此簡單

讀者可以使用該應用回饋想法。

Day 10: PhoneGap —— 開發手機應用如此簡單

什麼是 PhoneGap?

是一個自由開源的手機開發架構,可以使用标準的 web 技術,即 HTML,CSS 和 JavaScript 進行手機應用開發。

它将應用的web資源封裝在原生應用中,這些應用可以送出到各大應用商店。更重要的是,我們可以通過 PhoneGap 進行跨平台的手機應用開發。這意味着,在理想情況下,我們隻需編寫一次手機應用,就可以将該應用移植到多個平台。例如,我為Android裝置編寫了這個應用,然而使用

PhoneGap build

,我同時為其他裝置打包了應用。大多數标準的裝置特性,例如照相、地理位置、存儲等都由JavaScript API提供了。取決與目标裝置,PhoneGap提供的JavaScript API會有所不同。

關于 PhoneGap 的一些事實:

  1. 2009年,Nitobi開發了PhoneGap架構。
  2. 2011年10月,Adobe 購買了 Nitobi(PhoneGap)背後的公司。
  3. Adobe 将 PhoneGap 貢獻給 Apache 基金會。
  4. 此開源項目被命名為 Apache Cordova.
  5. PhoneGap 是 Adobe 完成的 開源項目 Apache Cordova 的實作。 PhoneGap的核心使用 Apache Cordova.
  6. PhoneGap使用基于插件的架構。所有裝置的特性以插件方式提供。本文中,我們将使用一些插件。

為什麼我關心PhoneGap?

  1. 沒有必要為每個平台學習一種原生的開發環境。如果開發者打算針對多個平台,那麼PhoneGap的跨平台特性可以節約開發者大量的時間和精力。我了解HTML、CSS和JavaScript,可以很平滑地進入手機世界。
  2. 對于開發基于REST API的CRUD手機應用,PhoneGap很好用。
  3. 它不強迫開發者選擇特定的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

提供了應用的顯示文本。

應用的檔案結構如下:

Day 10: PhoneGap —— 開發手機應用如此簡單

讓我們看下這些生成的目錄:

  1. merges

    檔案夾存放專門針對某個特定平台的資源。例如,我們可能使用

    merges

    來為Android裝置改變字型大小。
  2. platforms

    目錄存放平台建構檔案。
  3. plugins

    存放應用使用的插件。當我們安裝一個插件的時候,插件會被存放在這個目錄下。
  4. www

    目錄存放應用資源,例如

    html

    css

    js

    img

    。這是我們要花最多時間的目錄。

    config.xml

    包含了生成和分發應用所需的元資訊。元資訊也包括應用名稱、描述、作者詳情、應用權限等資訊。在 這裡 可以看到一個完整的清單。

運作下列指令即可在android上運作應用:

phonegap run android

這是我們的android平台閱讀器應用的第一次建構。如果有真實裝置連接配接,應用會跑在真實裝置上。如果沒有裝置,會啟動Android虛拟器,應用會被部署到虛拟器上。

Day 10: PhoneGap —— 開發手機應用如此簡單

注意: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

做了這些事:

  1. 綁定了文檔的就緒事件。一旦就緒,将渲染初始頁面。
  2. 初始頁面進行REST調用以擷取所有部落格。我們使用jQuery來調用REST。
  3. 接受資訊後,我們建立一個清單。最後重新整理下。

允許通路REST服務

PhoneGap預設不允許應用通路遠端資源。這就意味着應用無法進行REST調用。為了允許應用進行REST調用,我們需要允許它通路。我們可以通過通配符

*

允許應用通路一切資源。請參閱

文檔

了解更多資訊。

config.xml

下修改通路控制資訊:

<access origin="*" />

安裝插件

應用使用一些插件來通路特定裝置的特性。

cordova plugin add org.apache.cordova.geolocation

cordova plugin add org.apache.cordova.dialogs

  1. 第一個指令安裝了

    geolocation

    插件。Geolocation提供了裝置位置的資訊,例如經度和緯度。我們稍後将使用此特性。參閱
  2. 第二個指令安裝了

    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服務。一旦成功送出,我們顯示一個提醒。

運作應用

現在我們可以在裝置上安裝和運作應用了。使用如下指令: