Symbian開發(fā)之UI框架設(shè)計教程
我們曾經(jīng)向各位講過《Android UI設(shè)計教程》和《MeeGo設(shè)計教程》,但是目前使用最多的移動終端設(shè)備為Symbian操作系統(tǒng)。本文將向各位介紹一下《Symbian開發(fā)之UI框架設(shè)計教程》。所謂“應(yīng)用程序架構(gòu)”是指應(yīng)用程序框架類的集合。基于所需的UI設(shè)計,應(yīng)用程序可以具有稍微不同的架構(gòu),但是每種架構(gòu)都有一些公共部分,稱為“核心應(yīng)用程序類”
一、先看一下Symbian UI設(shè)計基礎(chǔ)
(1)核心應(yīng)用程序類.
◆所有的S60 UI應(yīng)用程序都具有一些基本功能:
◆提供一個用戶界面,用于顯示信息并允許用戶進(jìn)行交互
◆響應(yīng)各種用戶啟動的事件,比如用戶選擇一個菜單項
◆響應(yīng)系統(tǒng)啟動的不同事件,比如導(dǎo)致屏幕重繪的window服務(wù)器事件
◆能夠保存和恢復(fù)應(yīng)用程序數(shù)據(jù)
◆可以唯一性的向框架標(biāo)志自身
◆向框架提供有關(guān)應(yīng)用程序的描述性信息,比如圖標(biāo)和標(biāo)題等
這些類是:視圖(View)、文檔(Document)、應(yīng)用程序(Application)、應(yīng)用程序UI(Application UI)。
一個程序只能有一個文檔,可以有多個視圖。
(2)應(yīng)用程序初始化
必須創(chuàng)建下面的每個方法,才能提供最小的S60應(yīng)用程序:
a、所有S60 UI都實現(xiàn)一個全局函數(shù)E32DLL(),當(dāng)應(yīng)用程序啟動時,框架將首先調(diào)用該函數(shù),該函數(shù)也稱為DLL入口點,應(yīng)用程序必須存在該函數(shù)。每個S60 UI 應(yīng)用程序都是一個多態(tài)DLL。
b、讓框架調(diào)用NewApplication(),該函數(shù)是由DLL導(dǎo)出的唯一函數(shù)。
c、創(chuàng)建應(yīng)用程序類的一個實例,并返回它的指針,以后框架使用該指針完成應(yīng)用程序的創(chuàng)建。
d、由框架調(diào)用AppDllUid()返回應(yīng)用程序的UID。該函數(shù)必須返回在.mmp文件中指定的值,并且可用于確定應(yīng)用程序的實例是否正在運行。
e、框架獲取指向新創(chuàng)建Document類的指針,CreateDocumentL()。
f、NewL()具體去創(chuàng)建
g、礦見獲取AppUi類的指針,CreateAppUiL()。
h、由new (Eleave)CappUi()具體創(chuàng)建。
這樣一個最簡短直觀的框架就創(chuàng)建完畢。
(3)重要的AppUi方法:
AppUi提供了許多方法,框架可以調(diào)用這些方法通知每個應(yīng)用程序各種事件。
◆HandKeyEvent()用于處理用戶按鍵
◆HandleForegroundEventL()當(dāng)應(yīng)用程序切換到前臺或從前臺切換到后臺時調(diào)用該函數(shù),默認(rèn)的實現(xiàn)可以處理鍵盤焦點的改變。
◆HandleSystemEventL()傳遞由窗口服務(wù)器生成的事件
◆HandleApplicationSpecificEventL()可以自己定義的自定義事件的通知。默認(rèn)的實現(xiàn)可以處理顏色方案改變的通知。
◆HandleCommandL()用于處理用戶選擇的菜單項
(4)設(shè)計應(yīng)用程序UI
關(guān)于術(shù)語“視圖(view)”:
“視圖”是概念性的術(shù)語,含義是“數(shù)據(jù)模型在屏幕上的表示”,實際上由一個或多個從CcoeControl派生而來的UI控件實現(xiàn)視圖,這些控件按層次結(jié)構(gòu)進(jìn)行組織。父控件通常被稱為容器(Container),除了用于實現(xiàn)視圖的父控件,這種控件被稱為對話框(Dialog)。
在Avkon視圖切換架構(gòu)中,術(shù)語“Avkon視圖”指的是系統(tǒng)范圍內(nèi)的View服務(wù)器注冊的類,它控制視圖的實例化和析構(gòu)。
#p#
二、常見的Symbian應(yīng)用程序架構(gòu):
每種架構(gòu)都提供了設(shè)計應(yīng)用程序UI的不同方法――所有的架構(gòu)都提供了提交“視圖”或應(yīng)用程序數(shù)據(jù)可視化表示的方法,同時提供了一種用戶用來與架構(gòu)進(jìn)行交互的機制。
先簡單認(rèn)識一下:基于對話框的架構(gòu)和傳統(tǒng)的基于Symbian OS的架構(gòu)雖不相同,但和Avkon視圖切換架構(gòu)相比,這兩種架構(gòu)彼此更為類似。原因是:
◆它們的特征是它們用于生成視圖的UI控件類型。
◆架構(gòu)上幾乎相同。也就是說,在這兩種設(shè)計中,AppUi類簡單地“擁有”視圖控件,因此負(fù)責(zé)直接管理它們。
◆而Avkon視圖切換架構(gòu)從根本上不同于這兩種方法,它的視圖切換由系統(tǒng)范圍地View服務(wù)器來完成。
(1)基于控件的傳統(tǒng)Symbian OS控件
這些控件總是從CcoeControl直接繼承,用于表示從CcoeControl直接繼承的試圖類的標(biāo)準(zhǔn)術(shù)語是“容器”。
關(guān)于“CcoeControl”:
可以將CcoeControl認(rèn)為是一個空的帳篷。通過繼承這個類,可以創(chuàng)建各種各樣的自定義控件,自定義控件的功能和復(fù)雜性只受到程序員能力和想象力的限制。這種靈活性的唯一不利之處是,控件確實類似于一個空帳篷,因為需要進(jìn)行許多編碼工作來提供重要的功能。
在處理視圖切換方面,AppUi負(fù)責(zé)處理用戶發(fā)出的視圖切換請求。隨后,AppUi最終的行為類似于一個巨大的開關(guān),用于根據(jù)用戶或系統(tǒng)的輸入來激活或禁止容器。
注意:Container類從CcoeControl派生而來,CcoeControl是所有控件的基類。
在自己的容器類中必須實現(xiàn)從CcoeControl中的四個方法,框架將調(diào)用所有這些方法:
◆SizeChanged()允許控件響應(yīng)控件大小的改變
◆Draw()繪制控件
◆CountComponentControls()返回控件擁有的控件數(shù)量
◆ComponentControl()對于容器擁有的每一個控件,框架調(diào)用該方法獲取。
在AppUi類中按照如下代碼構(gòu)造容器:
- void ChelloWorldAppUi:::Control()
- {
- BaseControlL();
- IAppContainer=ChelloWorldContainer::NewL(ClientRect());
- IAppContainer->SetMopParent(this); //在控件之間建立父子關(guān)系,在容器上調(diào)用此方法。
- AddToStackL(iAppContainer); //將Container推入到控件棧頂,例如可以接收鍵事件
- }
注意:如果使用這種架構(gòu)實現(xiàn)帶有多個視圖的應(yīng)用程序,則通過使用AddToStackL()和RemoveFromStackL()在不同的容器之間切換。
(2)基于對話框的架構(gòu)
它不同于傳統(tǒng)Symbian OS架構(gòu)的是,它擁有的控件直接從對話框類家族繼承而來。
對話框的主要優(yōu)點是:相對于直接從CcoeControl派生而來的控件,它需要較少的開發(fā)工作,因為它們自動管理子控件的布局。
在AppUi類中完成構(gòu)造和運行:
- void CsimpleDlgAppUi::ConstructL()
- {
- BaseConstructL();
- IAppDialog=new(ELeave) CsimpleDlgDialog;
- IAppDialog->SetMopParent(this);
- IAppDialog->ExecuteLD(R_SIMPLEDLG_DIALOG);
- AddToStackL(iAppDialog);
- }
因為對話框是無模式的,ExecuteLD()將在調(diào)用后立刻返回。必須使用AddToStackL()將對話框添加到控件棧中,因為無模式的對話框無法自己完成這項工作。
還有,必須在AppUi的析構(gòu)函數(shù)中銷毀該對話框:
- CsimpleDlgAppUi::~CsimpleDlgAppUi()
- {
- if(iAppDialog)
- {
- RemoveFromStack(iAppDialog);
- delete iAppDialog;
- }
- }
(3)Avkon視圖切換架構(gòu)
比前兩種都復(fù)雜,引入另一個類作為AppUi和容器之間的媒介。另外,AppUi類從CAknViewAppUi繼承,而不是繼承于CaknAppUi。
前兩個架構(gòu),AppUi直接負(fù)責(zé)處理視圖切換,它必須管理視圖提交控件的實例化、刪除和顯示。但是,基于CaknView的類在這方面可以很明顯地減少AppUi地任務(wù)。
AppUi仍然處理視圖切換的請求,但現(xiàn)在,并不是刪除舊的容器并實例化新的容器,AppUi只需要調(diào)用它的其中一個特殊視圖激活函數(shù),如ActiveViewL()。這些特殊的CaknViewAppUi函數(shù)向View服務(wù)器提交一個激活請求,然后通過基于CaknView的相關(guān)類中的激活/禁止成員函數(shù),View服務(wù)器顯式地協(xié)調(diào)當(dāng)前視圖地禁止和所請求視圖的激活。
這種架構(gòu)所需的一般特性如下:
◆必須設(shè)計應(yīng)用程序,使每個CAknView派生的Avkon視圖擁有一個容器,然后AppUi擁有每個Avkon視圖。
◆必須從CaknViewAppUi派生應(yīng)用程序的AppUi,而不是從CAknView派生,這是因為前者提供了注冊、激活和禁止Avkon視圖的方法。
◆必須在View服務(wù)器中注冊所有的Avkon視圖。
◆Avkon視圖具有激活/禁止成員函數(shù),View服務(wù)器可以直接調(diào)用這些函數(shù)。必須重寫這些函數(shù),提供從屬容器的正確處理。
View服務(wù)器最主要的原則:確定在任意給定時刻,每個應(yīng)用程序中只有一個Avkon視圖被激活。Avkon視圖通過兩個UID向View服務(wù)器唯一性的標(biāo)志自己:一個UID用于標(biāo)志擁有該視圖的應(yīng)用程序,另一個UID用于在該應(yīng)用程序中唯一標(biāo)志該視圖。
對于每個基于CAknView的類,需要實現(xiàn)的激活/禁止函數(shù)是:DoActiveL()和DoDeactivate(),這些函數(shù)負(fù)責(zé)實例化和顯示或者刪除Avkon視圖擁有的UI控件。
View服務(wù)器將主動調(diào)用DeactivateView(),從而強制遵循每個應(yīng)用程序中只有一個激活視圖的規(guī)則。
如何使用Avkon視圖切換架構(gòu):
使用這種架構(gòu)時,必須結(jié)合使用CaknViewAppUi和CAknView類。每個Avkon類都從CAknView派生而來,并且必須包含一個Id()函數(shù),從而系統(tǒng)可以標(biāo)志這個類。它也必須實現(xiàn)DoActivateL()和DoDeactivateL()函數(shù)。此外,它還必須實現(xiàn)HandleForegroundEventL()、HandleCommandL()和HandleStatusPaneSizeChange()函數(shù),用于處理各種事件。
用戶請求激活視圖時,View服務(wù)器將調(diào)用DoActivateL()。該函數(shù)的目的是實例化并顯示提交視圖的控件。
注意:在DoDeactivateL()之前可以多次調(diào)用DoActivateL()。
將要禁止Avkon視圖時,則會調(diào)用DoDeactivateL(),該函數(shù)負(fù)責(zé)銷毀它的控件。當(dāng)應(yīng)用程序退出時,或者激活相同應(yīng)用程序的另一個視圖時,將禁止視圖。該函數(shù)絕對不能異常退出。
只有在激活A(yù)vkon視圖時才會調(diào)用HandleForegroundEventL(),即在調(diào)用DoActivateL()和DoDeactivateL()之間。當(dāng)視圖到達(dá)前臺時,接收HandleForegroundEventL(Etrue),當(dāng)從前臺移除視圖時,將接收HandleForegroundEventL(Efalse)。程序員可能希望使用這種方法來設(shè)置焦點或控制屏幕更新。
視圖菜單生成一條命令時,調(diào)用HandleCommandL(),因為狀態(tài)面板改變而使客戶矩形大小改變時,則調(diào)用HandleStatusPaneSizeChange ()。
為了讓Avkon視圖定義它自己的軟鍵和菜單資源,可以在資源文件(.rss)中創(chuàng)建一個AVKON_VIEW資源,然后將資源ID傳遞到視圖的BaseConstructL()函數(shù)中。
通常在AppUi對象的ConstructL()方法中構(gòu)造應(yīng)用程序中的所有Avkon視圖。使用AddViewL()在View服務(wù)器中注冊這些Avkon視圖,最終通過設(shè)置默認(rèn)的視圖來激活初始視圖,使用方法SetDefaultViewL()。
注意:不是由Avkon視圖處理的命令被傳遞到AppUi,在AppUi的HandleCommandL()方法里,只進(jìn)行視圖間切換的命令。本地視圖切換或者是應(yīng)用程序擁有的視圖切換,這些工作都通過引用目標(biāo)Avkon視圖的UID來執(zhí)行。
為了執(zhí)行外部視圖切換,則需要調(diào)用CcoeAppUi::ActivateViewL()函數(shù),提供一個包含目標(biāo)應(yīng)用程序UID和目標(biāo)視圖UID的TVWsViewId。如:
- const Tuid KphoneBookUid={0x101f4cce} ;// from PbkUID.h
- const Tuid kphoneBookContactViewUid={1};
- ActivateViewL(TvwsViewId(KphoneBookUid,KPhoneBookContactViewUid));
注意:如果自己的程序中的某部分視圖能夠被其他程序使用,那么我們必須通過導(dǎo)出為頭文件來發(fā)布應(yīng)用程序UID和視圖UID。
#p#
三、選擇適當(dāng)?shù)膽?yīng)用程序架構(gòu)
(1)使用Avkon視圖切換架構(gòu)
大多數(shù)情況下,這種架構(gòu)是最佳的架構(gòu),但它也具有局限性,如:視圖切換方案沒有任何內(nèi)置的方法來保存視圖切換的上下文。也就是說,沒有提供用于定位到前面激活視圖的機制,沒有類似于瀏覽器上后退功能的按鈕的功能。但是DoActivateL()確實收到了前面激活視圖的標(biāo)志符,因此可以自定義后退按鈕功能。
(2)使用基于控件的傳統(tǒng)symbian OS架構(gòu):
程序可能只需要一個視圖
應(yīng)用程序具有UI控件,必須保證這些UI控件的私有性。
如果是將應(yīng)用程序從不同的symbian OS平臺移植到Series 60。
(3)使用基于對話框的架構(gòu)
可以在資源文件中定義控件,讓對話框自動處理布局和繪畫,這比實現(xiàn)自定義繪畫行為更為容易。僅當(dāng)應(yīng)用程序的視圖之間沒有任何循環(huán)導(dǎo)航路徑時,才可以對這種應(yīng)用程序使用“基于對話框”的方法。
【編輯推薦】