使用Sencha Touch開發移動Web應用平臺
Sencha Touch 是由 Sencha 公司開發的移動 Web 應用開發框架,用以提升主流移動設備在瀏覽器上的觸碰操作,增強用戶體驗。該框架以久負盛名的 Ext JS 富客戶端框架為基礎,并支持最新的 HTML5 及 CSS3 標準,與流行的 Apple iOS 和 Andriod 設備兼容。一方面,它以 Webkit 瀏覽器引擎為基礎,提供了出色的性能和用戶體驗;另一方面,它提供了基于 GPL V3 許可的開源版本和詳盡的 API 文檔,體現了良好的開放性和易用性。因此,該框架可幫助移動應用開發人員提升開發效率,從而創造出更多富有創意的移動應用。
隨著智能移動設備的普及和 3G 通訊技術的發展,將會有越來越多的傳統應用部署為移動 Web 應用,而良好兼容性和操控性是 Web 應用成功的關鍵。本文將分為以下四個部分介紹 Sencha Touch 的獨特之處,并結合示例為相關移動應用的開發人員編寫良好兼容性和操控性的 Web 程序提供借鑒。
與眾不同的 Sencha Touch:功能和特性
Sencha Touch 是世界上第一個支持 HTML5 和 CSS3 標準的移動應用框架,你可以使用 HTML5 來編寫音頻和視頻組件,還可以使用 LocalStorage Proxy 來存儲離線數據,同時,大量 CSS3 樣式表為你提供了創建健壯樣式層的可能。該框架在提供豐富功能的基礎上對 JavaScript 庫文件進行合理優化,使得經過 gzipped 壓縮后的庫文件在 120kb 以下,最大限度地提升了 Web 應用在瀏覽器中的加載速度,增強了用戶體驗。
除了對最新標準的支持,該框架最大的特色正如其名,增強了對手持移動設備觸控操作的支持,除了支持瀏覽器標準的觸摸事件,還額外添加了如 tap, double tap, swipe, tap-hold, pinch 和 rotate 等富有吸引力的操作事件,使用戶體驗到與原生程序一樣的效果。
Sencha Touch 另一大優勢在于其跨平臺性,由于 Apple iOS 和 Andriod 設備有其獨立的開發、測試和運行環境,針對某一平臺開發的應用在另一平臺是不兼容的,這大大增加了移動應用的開發成本。而基于 Sencha Touch 開發的 Web 應用具有與原生應用相同的用戶體驗,同時兼容 Apple iOS、Andriod 和黑莓 RIM 6 設備,可以滿足大部分的市場需求。
此外,借助 Ext JS 多年來對 Ajax 數據集成的經驗,該框架提供了豐富的數據處理功能。開發人員能夠方便地處理各種格式的數據如 XML、JSON,并能靈活地綁定到可視化組件加以展示。
個性鮮明的 UI 組件
表單是用戶與應用程序交互的基本媒介,如用戶信息注冊、應用程序配置、個人評論的發表這些常見的 Web 應用場景都需要表單組件的支持。Sencha Touch 為我們提供了形式多樣、操作簡單的 表單組件 。圖 1 中第一個界面所展示的是基本表單元素,包括多種類型的輸入框,如純文本、密碼、郵件、URL 地址等類型,并能根據用戶輸入的文本進行有效性驗證,以減少開發者對用戶輸入格式的驗證代碼,同時,基本表單還支持單選、多選、日歷選擇、多行文本輸入等控件類型。圖 1 第二個界面展示了觸控屏手持設備所特有的滑動條組件,適合調整一些連續性的數值和作為切換的開關按鈕。圖 1 中第三個界面展示了建立在工具條上的表單控件,非常適用于搜索和文本過濾的應用場景。
圖 1 表單組件
列表是移動 Web 應用展示信息最為常見的組件,其中比較有特色的是分組列表(Grouped List)(如 圖 2 中第一個界面),它可以根據所列項目的首字母進行排序分組,當用戶觸摸屏幕右側字母索引時,屏幕可快速滾動并定位至對應分組,非常適合于列表信息較多的情況,如聯系人列表,歌曲列表等。圖 2 中第二個界面所示的嵌套列表(Nested List)則非常適合于展示信息有層級關系的情況,如瀏覽論壇時的“討論區 -> 主題帖 -> 原帖及回復內容”這樣的層次關系。
圖 2 列表組件
精致形象的圖標和布置合理的工具欄是 Apple iOS 原生應用引以為傲的部分,而 Sencha Touch 也可以做到這一點。圖 3 中第一個界面所示的是框架內置的圖標樣式,已可以滿足大部分應用的需要,開發人員還可以通過自定義圖標樣式來擴展出更多更豐富的圖標。如 圖 3 中第二個界面所示,圖標所在的工具欄布置方式也靈活多樣,即可在屏幕頂部或者底部,也可以多個層疊,并可以在工具欄上布置形狀各異的按鈕。圖 3 中第三個界面所展示的是根據底部 Tab 標簽頁而進行切換的面板,不同的面板中可以包含不同的主題內容。
圖 3 圖標、工具欄和標簽頁
如果你以為 Sencha Touch 只能做到以上這些小兒科,那就錯了,下面展示了一些高級的 UI 功能。圖 4 中第一個界面類似于 Apple iOS 設備上的 SpringBoard 操作,可以通過手指的左右或者上下滑動,來旋轉切換界面窗口;圖 4 中第二個界面顯示了一個窗口重疊的效果,當上層彈出窗口激活時,下層窗口的操作是被屏蔽的,在提醒用戶執行一些重要操作的場景中(如刪除或者保存),這樣的 UI 組件是非常好用的。
圖 4 旋轉切換與窗口的重疊效果
酷炫的動畫效果
一直以來,基于瀏覽器的 Web 程序動畫效果常被人詬病,尤其是基于 JavaScript 的動畫效果庫相對于原生的應用程序來說,還是存在一定的差距,而剛發布的 Sencha Touch 1.1.0 版本就支持多達六種動畫效果,分別是 cube、fade、flip、pop、slide 和 wipe。以最為酷炫的 3D 旋轉 Cube 動畫 為例,它將當前顯示的界面面板(稱之為 Card)想象為立方體的一個面,而即將展示的 Card 作為相鄰的另外一個面,以左上方的頂點作為旋轉基點進行旋轉,從而得到 Card 之間切換的動畫效果。大家一定很好奇它是如何做到這一點的,我們通過查看該動畫效果的源代碼即可找到答案。
圖 5 Cube 動畫效果示例
在文件 sencha-touch-1.1.0\src\core\Anim.more.js 中,可以看到以下代碼片段
清單 1
- this.from = {
- '-webkit-transform': 'rotate' + rotateProp + '(' + fromRotate + 'deg)' +
- (showTranslateZ ? ' translateZ(' + fromZ + 'px)': '') + fromTranslate,
- '-webkit-transform-origin': origin
- };
- this.to = {
- '-webkit-transform': 'rotate' + rotateProp + '(' + toRotate + 'deg)
- translateZ(' + toZ + 'px)' + toTranslate,
- '-webkit-transform-origin': origin
- };
由于 Sencha Touch 的動畫組件是基于 Webkit 核心的瀏覽器,所以其動畫效果實際上是基于 Webkit 的 3D 轉換引擎,代碼中 this.from 指的是當前 Card 如何旋轉消失的屬性,而 this.to 指的是要目標的 Card 如何旋轉得以呈現,具體 CSS 屬性的含義可參考 官方文檔 。
基于 Web 的博客瀏覽示例:應用開發環境的搭建、代碼結構及測試
隨時隨地獲取自己想要關注的信息是移動計算環境最直接的用途。本文將以一個簡單的博客訂閱與瀏覽程序為例,展示基于 Sencha Touch 進行移動 Web 應用開發的流程,幫助開發人員更快的熟悉該編程框架。博客訂閱與瀏覽應用的主要功能是訂閱自己關注的博客 RSS 源,瀏覽對應博客的主題列表,查看博文內容。為了實現以上需求,開發人員需要完成以下幾個步驟的工作。
#p#
搭建開發環境
第一,下載 Sencha Touch 庫文件 ,并將其解壓到本地目錄 %sencha-touch-home%;第二,進入 Eclipse Java EE IDE,創建一個名為 myblog 動態 Web 工程;第三,在本地安裝 Apache Tomcat 6.0.x ,在 eclipse 中將其配置為 Web server 并將 myblog 工程部署其中以備測試;第四,安裝 Andriod Virtul Machine 環境,用以啟動一個虛擬設備來測試 Web 應用的效果。需要說明的是,對于靜態的 Sencha Touch 工程,Tomcat 并不是必須的,可使用任意 HTTP Server 來部署應用,但由于本例中使用了 Servlet 解析 RSS 源來降低客戶端負載,因此采用了 Servlet 容器 Tomcat。
創建代碼結構
一個典型的 Sencha Touch 工程主要由幾個部分組成:sencha-touch 庫文件,JavaScript 文件,CSS 文件,圖標文件以及靜態 HTML 文件。sencha-touch 庫文件至少要包含默認的 CSS 文件 sencha-touch.css 和默認的 JavaScript 文件 sencha-touch.js,值得一提的是,為了便于在開發調試階段更準確地定位和解決問題,開發包中還包含了 CSS 和 JavaScript 對應的 debug 版本,開發人員可在開發階段使用該版本,而在產品部署階段再替換為對應的正式版。
圖 6 工程目錄結構
創建 HTML 和 JavaScript 文件
創建 Sencha Touch 應用的第一步就是創建一個 HTML 首頁文件用于鏈接 Sencha Touch 庫的 CSS 和 JavaScript 文件。我們博客瀏覽示例的 HTML 文件是 index.html,其內容如下:
清單 2
- <html>
- <head>
- <meta http-equiv="Content-Type" content="text/html; charset=utf-8">
- <title>My BLOG</title>
- <link rel="stylesheet" href="sencha-touch/resources/css/sencha-touch.css"
- type="text/css">
- <link rel="stylesheet" href="css/index.css" type="text/css">
- <script type="text/javascript" src="sencha-touch/sencha-touch-debug.js"></script>
- <script type="text/javascript" src="js/index.js"></script>
- </head>
- <body></body>
- </html>
當創建好 HTML 文件之后,接下來就需要創建應用程序的 JavaScript 文件 index.js,由于該示例是以瀏覽為主,因此選用 NestedList 組件作為 UI 界面的主體,相關代碼如下:
清單 3
- Ext.setup({
- icon : 'img/icon.png',
- tabletStartupScreen : 'img/tablet_startup.png',
- phoneStartupScreen : 'img/phone_startup.png',
- glossOnIcon : false,
- onReady : function() {
- .......
- var nestedList = new Ext.NestedList({
- fullscreen : true,
- title : '我的訂閱博客',
- displayField : 'text',
- dockedItems : [ topbar, bottombar ],
- store : store,
- getDetailCard : function(record, parentRecord) {
- return new Ext.ux.DescBox({
- value : 'Loading...',
- scroll : {
- direction : 'both',
- eventTarget : 'parent'
- }
- });
- }
- });
- ......
- });
- }
- });
可以看到,index.js 的第一行代碼調用了 Ext.setup() 方法,用以建立一個觸控設備的 Web 頁面,該方法可以為你的應用設置不同的啟動屬性和行為,例如示例代碼中的:
#p#
icon,設置該應用默認的圖標;
tabletStartupScreen,該屬性設置在平板電腦上的啟動圖標;
phoneStartupScreen,該屬性設置在智能手機上的啟動圖標;
glossOnIcon,該屬性設置是否在默認圖標上呈現光環效果;
onReady,該方法會在頁面加載完畢,瀏覽器中的 DOM 模型已經建立完成時被調用。由于為了保證程序在運行時所依賴的 JavaScript 文件都已經加載完畢,我們一般將應用啟動的邏輯置于該方法內,類似于 Java 程序的 main 方法。
在定義 NestedList 組件時,有四點值得我們注意:
界面布局:通過 dockedItems 屬性,指明了 NestedList 頂部和底部分別放置了工具欄 topbar 和 bottombar,topbar 主要用來便于用戶登錄和設置偏好信息,bottombar 主要是用來提供瀏覽博客時的一些常用操作,如訂閱新的 RSS 源,刪除選擇的博客,刷新博客列表,給好的博文加星推薦以及回復功能。為了生成工具欄,需要生成一個 Ext.Toolbar 對象的實例,以 bottombar 為例,其代碼如下:
清單 4
- var bottombar = new Ext.Toolbar({
- dock : 'bottom',
- defaults : {
- ui : 'plain',
- iconMask : true
- },
- scroll : 'horizontal',
- sortable : true,
- layout : {
- pack : 'center'
- },
- items : [ {
- iconCls : 'add',
- handler : function(btn, event) {
- addform.setCentered(true);
- addform.show();
- }
- }, {
- iconCls : 'trash'
- }, {
- iconCls : 'refresh'
- }, {
- iconCls : 'favorites'
- }, {
- iconCls : 'action'
- } ]
- });
該對象中主要定義了以下屬性:
dock,工具欄的放置位置,可選值有 top 和 bottom;
defaults,默認圖標的 UI 效果,其中 ui 指背景顏色的樣式,可選值有 dark,light 和 plain;
scroll,滾動方向,可選值有 horizon,vertical 和 both;
layout,表示工具欄圖標的布局方式,示例中表示的是居中排列。值得注意的是該屬性應該由一個 Object 對象來指定而不是 string;
items,該屬性用于指定一個數組,來表示工具欄中的圖標元素的集合,每個圖標對象至少需要有一個 iconCls 屬性來指定其樣式,而 handler 屬性則用于指定處理圖標點擊事件的方法,該方法回調時會傳入兩個參數 function(btn, event),第一個指當前被觸發事件的對象,第二個指被觸發的事件類型,本例中通過該方法彈出一個表單窗口用于提供給用戶輸入感興趣的博客 RSS 訂閱源。
圖 7 RSS 訂閱源添加表單
#p#
獲取數據:從后臺通過相關 API 獲取數據并展示在 UI 組件上,是實現 Web 應用的核心問題,Sencha Touch 組件一般都是通過指定 store 數據源對象來實現的。例如在本例中,采用 Ext.data.TreeStore 對象來定義在 NestedList 中層次化數據的獲取,其相關代碼如下:
清單 5
- Ext.regModel('ListItem', {
- idProperty : 'text',
- fields : [ {
- name : 'text',
- type : 'string'
- }, {
- name : 'link',
- type : 'string'
- }, {
- name : 'description',
- type : 'string'
- } ]
- });
- var store = new Ext.data.TreeStore({
- model : 'ListItem',
- proxy : {
- type : 'ajax',
- url : '/myblog/list',
- reader : {
- type : 'tree',
- root : 'items'
- }
- }
- });
首先,通過 model 屬性來指明返回數據的模型,該模型是通過 Ext.regModel() 方法來建立的,主要是為了告訴程序返回數據是什么結構;其次,通過 proxy 屬性來指明返回數據的獲取方式,該框架中主要有兩種 Proxy,Client proxy 和 Server proxy,Client Proxy 主要用于存儲本地數據,其子類有三個:
LocalStorageProxy,在瀏覽器支持的情況下將數據保存至 localStorage;
SessionStorageProxy,在瀏覽器支持的情況下將數據保存至 sessionStorage;
MemoryProxy,將數據保存在內存中,但是當頁面刷新時,數據都將會丟失。
Server proxy 主要用于存儲一些通過遠程請求服務器而獲取的數據,它包括:
AjaxProxy,發送一個 HTTP 請求到相同域的服務器;
ScriptTagProxy,使用 JSON-P 發送請求到不同域的服務器。
本例中采用的是最為常用的 Ajax 方式通過請求 servlet URL(/myblog/list) 來獲取 JSON 數據。
自定義組件:使用 NestedList 時,開發者要注意的是我們需要自己實現 getDetailCard() 方法,用于定義對葉子節點數據的查看 UI 組件。非常幸運的是,Sencha Touch 框架為我們提供了良好的擴展機制用于自定義組件,這為我們構建結構清晰、面向對象的 JavaScript 程序打下了基礎,示例中展示了如何擴展出一個自定義組件,代碼片段如下:
清單 6
- ExtExt.ux.DescBox = Ext.extend(Ext.Component, {
- ...
- afterRender : function() {
- Ext.ux.DescBox.superclass.afterRender.apply(this, arguments);
- thisthis.description = this.getTargetEl().createChild({
- tag : 'pre',
- html : this.value
- });
- },
- getValue : function() {
- return this.value;
- },
- setValue : function(description) {
- this.value = description;
- if (this.rendered) {
- this.description.update(this.value);
- }
- }
- });
我們定義了一個博客內容描述信息展示組件 Ext.ux.DescBox,它繼承自 Ext.Component 組件,并且自定義了 Get 和 Set 方法,同時重寫了父類的 afterRender 方法,其中第一行的代碼 Ext.ux.DescBox.superclass.afterRender.apply(this, arguments);必須調用,指的是將子類的參數應用到父類的構造方法中,類似于 Java 程序中的 super() 方法;第二行代碼 this.description = this.getTargetEl().createChild(...)指在 Component 組件中創建一個 HTML 標簽 pre, 并將 value 的值放置于 pre 標簽中。
事件處理:人機交互和 UI 組件之間的切換,需要事件來驅動或觸發,因此各 UI 組件都支持一系列特定的事件及其處理方法,可參閱 API 文檔 。示例中需要依靠葉子節點觸摸事件 leafitemtap 將顯示界面切換為 Detail Card,就需要在 NestedList 組件上注冊處理方法,其代碼為:
清單 7
- nestedList.on('leafitemtap', function(subList, subIdx, el, e,
- detailCard) {
- var ds = subList.getStore(), r = ds.getAt(subIdx);
- detailCard.setValue(r.get("description"));
- });
其回調函數的參數依次代表“觸控條目(item)所在 List 組件”,“觸控條目的 ID”,“觸控條目 element 對象”,“觸控事件對象”和“接下來要顯示的 Detail Card 組件對象”。
部署到 Apache Tomcat 6.x 進行測試
編寫好對應的 HTML、JavaScript 和后臺處理的 Servlet 之后,可將動態 Web 工程打包成為標準的 WAR 包,部署至 Tomcat 的 webapp 文件夾,啟動服務器;隨后打開 Android Virtual Devices,啟動其中的瀏覽器程序,并在 URL 地址欄輸入 http://<localhost IP address>:8080/myblog/,便可以對該應用進行測試了,運行的畫面如 圖 8 所示。由于 Sencha Touch 應用的跨平臺性,使用其他任意一款基于 Webkit 內核的瀏覽器,如 iPhone4 的 Safari,也能得到相關的測試結果,而不僅限于文中示例的 Android 設備瀏覽器。
圖 8 應用運行及測試畫面
開發與原生程序一樣酷炫界面的 Web 移動應用,一直是 Web 開發者的夢想,Sencha Touch 移動 Web 開發框架使得這一夢想不再遙遠。該框架以其豐富的 UI 組件,個性化的動畫效果,穩定的數據及事件處理機制,易擴展的編程模型,在移動 Web 應用這個新領域嶄露頭角。本文利用一個博客瀏覽程序簡要介紹了利用該框架編寫程序的流程及基本方法,然而距離一個成熟的 Web 應用還有相當的距離,如用戶登錄的安全性、多用戶并發時后臺的伸縮性以及客戶端 JavaScript 的性能都有待提高,因此值得廣大開發者深入的學習及實踐。