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


























