2011年9月3日 星期六

Android 開發筆記 - 使用 PhoneGap、jQuery Mobile 與台北市政府開放資料(Open Data)

我本身是 Native App Development 崇尚者(雖然沒啥開發經驗),理由很簡單,我樂於學習新的開發環境,想要 app 的效能最快,過去也曾探問過一些人的意見,有些大老闆就是力推 Native App ,理由就是要給使用者最好的使用經驗。然而,我的強者同事一直推薦 PhoneGap 的架構,他的理念是希望可以用一套固定且可延續性的學習經驗,應用在任何 mobile app 開發情境,這時就完全屬於 PhoneGap/HTML 的絕對領域!我也能體會在某個情境下並不太需要高效能的追求,這時候就可以好好善用 PhoneGap 所提供的便利性,只要是 Web Programmer 背景,會 HTML + JS + CSS 即可,剩下的只是安裝 app develpment 環境並看著 PhoneGap 使用手冊,也就行啦!


由於沒啥動力,就找了台北市政府九月才提供的公開資料(Open Data)當作範例,剛好可以透過 Javascript API 取得資料,再加上這類屬於查詢及資料呈列功能,不會有多嚴重的效率,所以,衝一發!


首先不厭其煩還是要裝一下 Android 開發環境,需要的可以逛一下 :Android & Eclipse 開發環境 - 第一次安裝筆記


接著從 Eclipse 建立一個 Project ,在此命名為 MyOpenData:


Application name: MyOpenData
Package name: tw.taipei.opendata
Create Activity: MyOpenDataActivity
Min SDK Version: 10 


接著從 PhoneGap 官網下載相關檔案,並依照使用手冊安裝:



  1. 建立 /libs、/assets/www 目錄,將 phonegap-1.0.0.js 複製到 /assets/www,將 phonegap-1.0.0.jar 複製到 /libs,將 xml 複製到 /res 目錄中。

  2. 更新 MyOpenDataActivity.java


    • package tw.taipei.opendata;

      import com.phonegap.*;
      //import android.app.Activity;

      import android.os.Bundle;

      public class MyOpenDataActivity extends DroidGap {
          /** Called when the activity is first created. */
          @Override
          public void onCreate(Bundle savedInstanceState) {
              super.onCreate(savedInstanceState);
              //setContentView(R.layout.main);
              super.loadUrl("file:///android_asset/www/index.html");

          }
      }



  3. 在 /libs 目錄按右鍵> Build Path > Configure Build Path > Libraries > Add JARs > 選 phonegap-1.0.0.jar 。

    • add_libs_phonegap



  4. 更新 AndroidManifes.xml:

    • <supports-screens
      android:largeScreens="true"
      android:normalScreens="true"
      android:smallScreens="true"
      android:resizeable="true"
      android:anyDensity="true"
      />
      <uses-permission android:name="android.permission.CAMERA" />
      <uses-permission android:name="android.permission.VIBRATE" />
      <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
      <uses-permission android:name="android.permission.ACCESS_LOCATION_EXTRA_COMMANDS" />
      <uses-permission android:name="android.permission.READ_PHONE_STATE" />
      <uses-permission android:name="android.permission.INTERNET" />
      <uses-permission android:name="android.permission.RECEIVE_SMS" />
      <uses-permission android:name="android.permission.RECORD_AUDIO" />
      <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
      <uses-permission android:name="android.permission.READ_CONTACTS" />
      <uses-permission android:name="android.permission.WRITE_CONTACTS" />
      <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
      <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
      <uses-permission android:name="android.permission.GET_ACCOUNTS" />

    • <activity android:name="com.phonegap.DroidGap" android:label="@string/app_name" android:configChanges="orientation|keyboardHidden">
      <intent-filter> </intent-filter> 
      </activity>

    • 如此圖:
      androidmanifest_phonegap



  5. 在 /assets/www 目錄中,新增想要的資料即可,如 index.html 


    • <!DOCTYPE HTML>
      <html>
      <head>
      <title>PhoneGap</title>
      <script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>
      </head>
      <body>
      <h1>changyy.pixnet.net</h1>
      </body>
      </html>



  6. 目錄結構

    • project_phonegap



  7. 成果:

    • phonegap result




接著來使用 jQuery Mobile ,透過這可以很便利地製作 Mobile App UI 及其事件操作,例如仿 iOS UITableViewController 等:



  1. 下載 Javascript 函式庫和 CSS,加入 /assets/www 裡 (此例沒用到,改用外部資源)



  2. 更改 HTML 檔案,此例為 index.html

    • <link href="http://code.jquery.com/mobile/latest/jquery.mobile.min.css" rel="stylesheet" type="text/css" />
      <script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
      <script src="http://code.jquery.com/mobile/latest/jquery.mobile.min.js"></script>



  3. 新增 header、table 和 footer 效果

    • <!DOCTYPE HTML>
      <html>
      <head>
      <meta name="viewport" content="width=device-width, initial-scale=1"> 
      <title>changyy.pixnet.net</title>
      <script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>
      <link href="http://code.jquery.com/mobile/latest/jquery.mobile.min.css" rel="stylesheet" type="text/css" />
      <script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
      <script src="http://code.jquery.com/mobile/latest/jquery.mobile.min.js"></script>
      </head>
      <body>
      <div data-role="page">
      <div data-role="header">
      <h1>Page Title</h1>
      </div><!-- /header -->
      <div data-role="content">
      <p>Page content goes here.</p>
      </div><!-- /content -->
      <ul data-role="listview" data-theme="g">
      <li data-role="list-divider">changyy.pixnet.net</li>
      <li><a href="ios.html">iOS</a></li>
      <li><a href="android.html">Android</a></li>
      </ul>
      <ul data-role="listview" data-theme="g">
      <li data-role="list-divider">MyOpenData</li>
      <li><a href="1.html">PhoneGap</a></li>
      <li><a href="2.html">jQuery Mobile</a></li>
      <li><a href="3.html">Data.Taipei</a></li>
      </ul>
      <div data-role="footer">
      <h4>Page Footer</h4>
      </div><!-- /footer -->
      </div><!-- /page -->
      </body>
      </html>



  4. 成果圖:

    • phonegap_result2




接著使用 台北市政府開放資料 所提供的 Open Data API:



  1. 目前 2011-09-03 共有 131 項資料可使用,在此挑選 "臺北市健保特約藥局"


  2. 成果:

    • 健保特約藥局_1
      健保特約藥局_2



  3. HTML 程式碼:

    • <!DOCTYPE HTML>
      <html>
      <head>
      <meta name="viewport" content="width=device-width, initial-scale=1">
      <meta http-equiv="Content-Type" content="text/html; Charset=UTF-8" />
      <title>changyy.pixnet.net</title>
      <script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>
      <link href="http://code.jquery.com/mobile/latest/jquery.mobile.min.css" rel="stylesheet" type="text/css" />
      <script src="http://code.jquery.com/jquery-1.6.2.min.js"></script>
      <script src="http://code.jquery.com/mobile/latest/jquery.mobile.min.js"></script>
      <script language="Javascript">
      $(document).ready(function(){
      $.ajax({
      type: "POST",
      url: "http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/G67AHAMEDICATION10007/",
      data: "format=json&callback=yourCallback",
      dataType: 'jsonp' ,
      });
      });
      var regionTitle = new Array();
      var regionData = new Array();
      var regionHash = [];
      function yourCallback(data)
      {
      //console.log(data);
      var container = $('#output');
      regionTitle = new Array();
      regionData = new Array();
      regionHash = [];
      for( var i=0 ,cnt=data['d'].length ; i<cnt ; ++i )
      {
      var getRegion = data['d'][i]['地址'].substring(0,data['d'][i]['地址'].indexOf('區',0)+1);
      if( regionHash[getRegion] == undefined )
      {
      regionHash[getRegion] = regionData.length;
      regionData.push(new Array());
      regionTitle[regionHash[getRegion]] = getRegion;
      }
      regionData[regionHash[getRegion]].push(data['d'][i]);
      }
      container.empty();
      for( var i=0 ,cnt=regionData.length ; i<cnt ; ++i )
      {
      //console.log( '@'+i+',Title:'+regionTitle[i]+',Cnt:'+regionData[i].length );
      container.append( '<li><a href="#'+regionTitle[i]+'">'+regionTitle[i]+'藥局資料<span>'+regionData[i].length+'</span></a></li>' );

      var subpage = '';
      for( var j=0,subCnt=regionData[i].length ; j<subCnt ; ++j )
      {
      //container.append( '<li><h3>'+regionData[i][j]['機構名稱']+'</h3><p>'+regionData[i][j]['地址']+'</p></li>' );
      subpage += '<li><h3>'+regionData[i][j]['機構名稱']+'</h3><p>'+regionData[i][j]['地址']+'</p></li>';
      }
      $('body').append(
      '<div data-role="page" data-url="'+regionTitle[i]+'">'+
      '<div data-role="header" data-position="fixed">'+
      '<h1>'+regionTitle[i]+'</h1>'+
      '</div>' +
      '<ul data-role="listview" data-theme="g">'+
      subpage +
      '</ul>' +
      '<div data-role="footer" data-position="fixed">' + 
      '<h4><a href="#home">Back</a></h4>' +
      '</div>' +
      '</div>'
      );
      }
      $('ul').listview('refresh');
      }
      </script>
      </head>
      <body>
      <div data-role="page" id="home">
      <div data-role="header" data-position="fixed">
      <h1>健保特約藥局</h1>
      </div><!-- /header -->
      <ul id="output" data-role="listview" data-theme="g"><center><h3>Loading...</h3></center></ul>
      </div><!-- /page -->
      </body>
      </html>




心得:


PhoneGap 的原理就是把開發者寫的 HTML + JS + CSS 用 Web 元件跑起來,在 Android 稱作 WebView ,在 iOS 稱作 UIWebView,因此,你也可以不需要 PhoneGap ,自行學樣包起來也沒問題。


使用 HTML 最大的優點是 UI 方面可以由 jQuery Mobile 包辦,這樣就算沒有像 iOS 漂亮的開發介面,也可以輕輕鬆鬆在 Android 或其他 mobile 平台達成。並且 UI 刻制對熟悉 CSS 的人來說,更是如魚得水啊。


最大缺點:


由於程式操作狀態從 Native app 移至 Web 元件上頭,當 native app 狀態改變時,不見得能對應到 Web 元件的狀態,例如偵測網路狀態或切換程式的動作等,有時希望每次一啟用程式,無論是從背景運作或是暫停模式中起來,都想要恢復至到一個畫面時,這時候 Web 元件就不見得會聽話。解法應該也是有的,只是解來解去會變成寫 native app 了。


6 則留言:

  1. 您好
    我想請問一下
    因為台北市政府的API改了
    您所寫的3.HTML 程式碼:
    跟著也不能使用
    我是剛剛想學怎麼寫的新手
    因為沒得比對台北市政府的舊API資料
    所以..就看不懂您寫的語法了
    可以麻煩您指教一下嗎?


    版主回覆:(08/18/2011 01:07:47 PM)


    function yourCallback(data) 是 query 完 API 所吐回來的資料會存在 data 中,可以是透過 console.log( data ) 來查看 API 回傳的資料結構,另一種則是直接去連 API 來看: http://taipeicityopendata.cloudapp.net/v1/TaipeiOGDI/G67AHAMEDICATION10007/?format=json&callback=yourCallback ,接著 data['d'] 就是取得一個 array,而 data['d'][0]['地址'] 是第一個項目的地址,剩下的自己在推吧。

    查看的結果,覺得 API 格式好像沒變耶??若有其他問題,可以直接把 HTML 存起來(把<script type="text/javascript" charset="utf-8" src="phonegap-1.0.0.js"></script>拿掉就行了),用 firefox 的 firebug 或 Chrome 檢查元素來查看哪邊有問題。

    回覆刪除
  2. 因為我按照您的
    3.html程式碼
    copy出來值行會一直在loaning這畫面不動
    url:也有改成目前台北市政府的url
    phonegap-1.0.0.js也有改成我抓到的版本
    所以我就不知道是怎麼一回事了= =||
    謝謝您~我再試試看

    回覆刪除
  3. 你最後幾句話就是說只能某些方面可以用mobile framework來做,大部分狀況跟NativeApp不能比囉?但是我們都知道所謂的user都是很挑剔的,這樣的技術能應付現實嗎?還是只能開發那些躲在角落的功能而已?

    版主回覆:(02/07/2012 12:16:19 AM)


    若你實際用過 web/mobile framework 開發出來的 mobile app (iOS app 或 Android app),就比較能感受出來 cc 有的甚至結合的還不錯(不靠像 jquery mobile ui 而是自己規劃架構)

    如果你的 app 目標就只是查詢、顯示資料類(例如成列文字類、簡易圖表等),那用 web/mobile framework 應該還算夠用。最好的實例就是 facebook app 的情況,好一陣子都是用 web 方式開發 mobile app,對大部分的情況都很夠用,但是牽扯到多種畫面的更新或等待 server 吐出來給 mobile 端再交給 web 呈現時,就會出現稍微的卡卡現象。現在 facebook app 改成 native app 模式,理所當然 native 的效能就出來了,得到的好評更多上許多。

    至於使用者很挑剔的部份嘛...只能說挑剔的就算用 native app 也還是會挑,不挑的就算你只用醜醜的 web 介面,他覺得有得到他想要的資訊、功能,一樣會有耐心等待的 :D

    btw, 我個人是覺得...如果你的底子是 web programmer 的話,那可以嘗試用這類開發你所需要的應用程式看看。假設你的背景不是 web programmer 也不排斥學新的東西,那直接去學 native app 開發,等你熟的時候不見得會比 web 開發方式慢喔!

    回覆刪除
    回覆
    1. 躲在角落...基本上你 ui 刻的好 user 根本沒感覺,espn 夠大吧,他們的 app 就是 base on webview,好用的很

      刪除
    2. 躲在角落...基本上你 ui 刻的好 user 根本沒感覺,espn 夠大吧,他們的 app 就是 base on webview,好用的很

      刪除
  4. 補充一下,phonegap跟webview包裝的差異在phonegap可以執行硬體,webview無法,thanks

    回覆刪除