將簡單的 HTML 網站轉為行動 App (下)
你是不是每天都有一堆代辦事項等著處理呢?在看完了《將簡單的 HTML 網站轉為行動 App (上)》之後,緊接著要讓你知道從 Stage 2 開始的後續 App 建構方式。
Stage 2
在建構 App 的第二階段,我們會透過 Brick 提升 App 的使用經驗。與其教你「捲動一堆課程計畫才找到自己需要的課程」,我們另外建構了 Brick 自訂元素,可於同一畫面顯示不同的計畫。
可到 Github 上觀看 Stage 2 完成的程式碼。
- 先執行下列指令,將整個 Brick 程式碼安裝到
app/bower_components
目錄之中。bower install mozbrick/brick
- 我們只會使用 Brick 的
brick-deck
組件。此組件會提供如「卡牌堆疊」形式的介面,單次僅顯示一張brick-card
而隱藏其他卡牌。要使用此組件時,需將下列程式碼添增到index.html
檔案的,就能匯入
brick-deck
組件的 HTML 與 JavaScript:
- 接著所有的學期計畫都將封包到
自訂元素之中,且各個獨立計畫都包到
自訂元素內。最後的結構應該像這樣:
brick-deck
組件必須設定與
元素達 100% 的高度。將下列程式碼添增到
css/index.css
檔案:html, body {height: 100%}
- 當你執行 App 時,就會顯示第一張卡牌而隱藏剩下的卡牌。我們要新增某些 JavaScript 才能達到上述效果。首先添增某些
元素,以連接必要的 JavaScript 檔案至 HTML:
cordova.js
具備通用的 Cordova 特有協助函式,而index.js
則具備我們 App 的專屬 JavaScript。index.js 已內含一組app
變數的定義。只要呼叫app.initialize()
隨後就執行該 App。最好是載入window
時呼叫此物件,所以添增下列:window.onload = function() { app.initialize(); }
- Cordova 新增了幾項事件,而在載入並啟動所有的 Cordova 程式碼之後,就會發動其中之一的
deviceready
。我們將App 的主要行動碼放進此事件的回呼 (callback) ─app.onDeviceReady
之中。onDeviceReady: function() { // starts when device is ready },
- Brick 將對其所有元素添加數項函式與屬性。本範例則添增了
loop
與nextCard
到
元素。由於其內含了id="plan-group"
屬性,因此最好是透過document.getElementById
從 DOM 取得該元素。接著想在發動touchstart
事件時切換卡牌,就從app.nextPlan
回呼來呼叫nextCard
。
Stage 3:
我們要在第三階段新增選單列,且該選單列需顯示目前計畫的名稱,以達到更多用途。可到 GitHub 上觀看 Stage 3 完成的程式碼。
- 我們要使用 Brick 的
brick-tabbar
組件來建構選單列。首先必須匯入該組件,將下列指令碼加到 HTML 的:
- 接著為所有的卡牌添加一組 ID,並納入卡牌作為
brick-tabbar-tab
元素的目標屬性值,就如下列:Angelica Andrew ...
- Brick 會在背景中使用分頁的
reveal
事件,以呼叫 Deck 的nextCard
函式。只要觸碰分頁列元素就會變更卡牌。在 App上就更簡單了。如果你看到這裡想直接結束線上教學,只要從index.html
檔案中,將連結 index.js 與 cordova.js 的元素移除即可。
Stage 4
如果要進一步提升觸控式裝置上的使用經驗,也可以加入左/右滑動的手勢來觀看卡牌。可到 GitHub 上觀看 Stage 4 完成的程式碼。
- 現由
tabbar
組件進行卡牌切換。如果要讓已選擇的分頁能對應顯示目前的card
,就必須使之連動。只要監聽各個card
的「show
」事件即可達到此功能,即針對儲存於app.planGroupMenu.tabs
中的各分頁加上:tab.targetElement.addEventListener('show', function() { // select the tab });
- 考慮到競態條件 (Race Condition,當啟動 App 時,
planGroupMenu.tabs
可能不存在),我們會使用輪詢 (Polling) 直到時機正確,再指派事件處理器:function assignTabs() { if (!app.planGroupMenu.tabs) { return window.setTimeout(assignTabs, 100); } // proceed
分頁連接卡牌的整個部分就如同以下所示:onDeviceReady: function() { app.planGroupMenu = document.getElementById('plan-group-menu'); function assignTabs() { if (!app.planGroupMenu.tabs) { return window.setTimeout(assignTabs, 100); } for (var i=0; i < app.planGroupMenu.tabs.length; i++) { var tab = app.planGroupMenu.tabs[i]; tab.targetElement.tabElement = tab; tab.targetElement.addEventListener('show', function() { this.tabElement.select(); }); } }; assignTabs(); // continue below ...
- 在 Firefox OS App 中測試單指滑動其實很簡單。共需要兩組回呼監聽
touchstart
與touchend
事件,並計算pageX
參數上的「差」值即可。但可惜 Android 與 iOS 都不會因手指的動作來發動touchend
事件。而我們將藉由監聽touchmove
事件來得知手指的明顯移動,但僅限由scroll
事件攔截到才會發動該事件。最好是呼叫touchmove
回呼中的preventDefault()
,即可停止發動該事件。如此一來就可關閉scroll
,並如我們所需的執行該功能:// ... continuation app.planGroup = document.getElementById('plan-group'); var startX = null; var slideThreshold = 100; function touchStart(sX) { startX = sX; } function touchEnd(endX) { var deltaX; if (startX) { deltaX = endX - startX; if (Math.abs(deltaX) > slideThreshold) { startX = null; if (deltaX > 0) { app.previousPlan(); } else { app.nextPlan(); } } } } app.planGroup.addEventListener('touchstart', function(evt) { var touches = evt.changedTouches; if (touches.length === 1) { touchStart(touches[0].pageX); } }); app.planGroup.addEventListener('touchmove', function(evt) { evt.preventDefault(); touchEnd(evt.changedTouches[0].pageX); });
敬請期待
我們規劃中的下一篇文章,將說明這個 App 如何加入 Marketplace 與其後續下載步驟。敬請期待!
原文連結:Creating a mobile app from a simple HTML site