Symbian學習筆記(3)——應用程序框架
不管是哪一種模板,它們共同的部分是入口函數、Application和Document。即以下三個文件是所有工程都具有的:
1)有一個與工程同名的cpp文件,它只提供了兩個全局函數,是程序執行的入口,負責創造Application。這個文件我們不用管它。
2)有一個以工程名+Application命名的類,派生于CAknApplication,它負責創建文檔類并提供應用的UID。一般情況下它也不需要我們操心,但是因為它提供了一個函數OpenIniFileLC,如果我們需要在啟動程序時加載ini文件中的配置,可以重載它。
3)有一個以工程名+Document命名的類,派生于CAknDocument,它負責創建Ui類,同樣一般情況我們不管它,但是它也提供了一個函數OpenFileL,如果需要加載普通文件,也可以重載它。
下面來看看兩種模板的不同點吧。
一. 基于CCoeControl的常規模式
這種模式的特點是一個AppUi類并且對應一個Container類。
它在上述三個文件以外,還有下面兩個文件:
4)有一個以工程名+AppUi命名的類,派生于CAknAppUi,它主要的工作是負責用戶接口(所謂的UI)并且創建Container類,所以它是我們關注的重點之一。
它最重要的成員函數是HandleCommandL,這個函數來自于CEikAppUi類,負責處理各種命名/事件。
有兩個函數DynInitMenuBarL和DynInitMenuPaneL,如果需要動態更改菜單的項目,可以重載它們。
HandleKeyEventL函數則可以在需要自己處理鍵盤事件時重載一下。
此外,這個AppUi還提供一些比較常用的函數,比如:
Document() 可以獲取Document對象指針。
Application() 可以獲取Application對象指針。
StatusPane() 可以獲取狀態欄的指針。
Cba() 可以獲取控制欄的指針。
5)有一個以工程名+Container命名的類,派生于CCoeControl,它負責內容的展示,也是我們關注的重點。
如果需要在界面上增加控件類類的東西,都是在這個類中實現,總的來說,它負責所有與界面展示相關的東西。
它有一個Draw函數,但是如果我們是通過控件來展示信息,則這個函數里的代碼似乎與我們關系不大,除非我們的界面完全是靠畫出來的。
另兩個函數ComponentControl和CountComponentControls分別獲取控件與獲取控件個數,在依賴于控件展示的GUI應用中則更為重要。
因為它實現了接口MCoeControlObserver,所以函數HandleControlEventL也需要實現一下。
利用它的成員iCoeEnv可以取到AppUi類的指針,不過需要強制轉型一下,如:STATIC_CAST(CiMusicAppUi*,iCoeEnv->AppUi())->...
二. 基于CAknView的MVC模式
這種模式的特點是在AppUi類與Container類之間增加一個AppView的類,即一個AppUi對象,N個AppView和N個Container。
除了公共的三個文件以外,它包括的文件有:
4) 有一個以工程名+AppUi命名的類,派生于CAknViewAppUi類,其實也是間接派生于CAknAppUi類,它的工作職責與常規模式中的AppUi類也基本相同。
***的區別在于它不是直接創建Container類,而是創建AppView類,并且是創建多個AppView類。同時它還需要負責將創建的view加入視圖棧里AddViewL。
CleanupStack::PushL( view1 );
view1->ConstructL();
AddViewL( view1 ); // transfer ownership to CAknViewAppUi
CleanupStack::Pop(); // view1
CiMusicViewFavt* view2 = new (ELeave) CiMusicViewFavt;
CleanupStack::PushL( view2 );
view2->ConstructL();
AddViewL( view2 ); // transfer ownership to CAknViewAppUi
CleanupStack::Pop(); // view2
CiMusicViewWeb* view3 = new (ELeave) CiMusicViewWeb;
CleanupStack::PushL( view3 );
view3->ConstructL();
AddViewL( view3 ); // transfer ownership to CAknViewAppUi
CleanupStack::Pop(); // view3
this->ActivateLocalViewL(KViewMusicId);
5)有多個以工程名+View命名的類,派生于CAknView類。它負責分擔AppUi部分事件的處理,所以,它也有HandleCommandL函數。
此外,它的DoActivateL和DoDeactivate兩個函數在當前視圖激活或失活時被調用,需要重載一下。
在激活時,需要創建視圖對應的Container類,并且調用Container的SetMopParent為自己,還要在上層的AppUi中將這個Container加入棧中。一般代碼如下:
iContainer->SetMopParent(this);
iContainer->ConstructL( AppUi()->ApplicationRect() );
iContainer->listType=type;
AppUi()->AddToStackL( *this, iContainer );
注意,它的AppUi()可以得到它的上層的AppUi對象指針。
失活時則正好相反,需要
...{
AppUi()->RemoveFromViewStack( *this, iContainer );
}
delete iContainer;
iContainer = NULL;
6)有多個以工程名+Container命名的類,派生于CCoeControl并實現接口MCoeControlObserver,所以它的行為與常規模式中的Container類似。
這種模式可以有效地組織應用程序,根據應用的多個功能界面切割成數個模塊(視圖)。由多個AppView來分擔AppUi中的事件處理,負責自己這個視圖下的具體行為與顯示。
各個視圖之間的切換也很簡單:
STATIC_CAST(CiMusicAppUi*,iCoeEnv->AppUi())->ActivateLocalViewL(KViewMusicId);
[補充]
三 比較兩種模式
有些教材還提出過對話框模式,但是我覺得那似乎不太實用,也沒注意過它的結構組成。
對比上述兩種架構模板,很顯然,第二種基于視圖的模板應該更有實用性一點,除非程序實在簡單。
不過,這里的“視圖”很容易讓人產生誤解。一般我們說MVC的時候,模型-視圖-控制器,但是這里的AppView其實對應的控制器,而Container對應的則是視圖。
所以在新的SDK里,***種模板中由向導生成的工程中源于CCoeControl的類名改成了AppView,對應繼承于CAknAppUi控制器的類名叫AppUi。因為我沒有看到多視圖的工程生成的代碼,如果這樣的話,估計也應該對應的改一下吧。
【編輯推薦】