博客园丁

我是博客园的一丁,我会永不停顿,不停创新。
  首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

Symbian开发学习笔记之一

Posted on 2006-09-11 17:48  Jason.Jiang  阅读(2001)  评论(2编辑  收藏  举报
Symbian中的应用程序通常分为两个部分,engine和UI,如此的划分增加了可维护性和灵活性,engine就好象是程序的核心,它主要处理运算和数据,而UI(应该是User Interface)主要处理数据的显示和所有行为(操作行为应该是)。

engine不谈,它是程序起作用的灵魂,这个是因程序而异的,我们来看应用程序外观,它可以分为三种体系结构:
一、传统的symbianOS控制体系结构
二、基于对话框的体系结构
三、视图体系结构

看看好象跟MFC搞的似的,也有个View和Dialog,不过也是Symbian就是用C++写的,面向对象特性非常好。

运用什么样的界面取决于程序和界面布局的需要,就不多说了。只是不管你使用哪种,都是从一个基类继承而来的,就好象是CView一样。

[传统SymbianOS应用程序的体系结构]
传统意义上,SymbianOS应用程序是在CCoeControl类的基础上派生出我们自己的view controls,这些都存放在应用程序的control stack中,也就是我们应用程序的视图。这些controls会根据应用程序的需要来创建释放或显示隐藏,以产生相应的操作。

[Dialog体系结构]
如果主体应用是对话框,那我们更应该使用这样的体系结构,使用dialogs的好处是我们光可以靠改变resource文件来修改内容和布局,而不需要重新编译那c++代码。

注意,如果不小心规划,那嵌套的对话框将耗去大量的堆栈空间。

另外,如果dialog体系结构被用于主要视图,那建议讲其设计为非模态对话框。(它不垄断用户的输入,用户打开非模态对话框后,仍然可以与其它界面进行交互。)如果你要生成一个主界面对话框,要求是满屏,并提供多页设计,那应该包括如下features:
RESOURCE DIALOG r_dlgapp_main_dialog
{
flags = EEikDialogFlagNoDrag | EEikDialogFlagNoTitleBar |
EEikDialogFlagFillAppClientRect |
EEikDialogFlagCbaButtons | EEikDialogFlagModelss;

buttons = r_dlgapp_softkeys_options_home;
pages = r_dlgapp_main_pages;
}

如果要创建一个对话框应该如下:
void CDlgappAppUi::ConstructL()
{
BaseConstructL();
iAppView = new(ELeave) CDlgAppMainView;
iAppView->ExecuteLD(R_DLGAPP_MAIN_DIALOG);
AddToStackL(iAppView);
}

ExecuteLD()在调用后立即返回,并且对话框必须要加到control stack中——使用AddToStack函数,因为非模态对话框不会自己处理这些。

[View体系结构]
使用view的应用程序每次只能有一个活动的view,当另一个view要激活时,当前的view就要被释放。当一个view被释放后,所以的菜单,对话框以及包含的应用都将被关闭。
每个view都被当作一个应用UI对待,它必须提供一个Id()函数以便为系统所标识,它也要重载DoActivateL(),DoDeactivate(),HandleForegroundEventL(),HandlCommandL()和HandleStatusPaneSizeChange()函数以处理各种事件。

下面一个个的看
DoActivateL()
当客户端要求你的view激活时,它就要被调用。client可能发送消息参数给你的view,如果你的view已经是激活了,那只有当client明确要求再次激活时才被调用,所以你的DoActivateL()实现得应付这种情况,ie已经激活了view。如果你不打算显示view或者你的view不想处理任何消息,那一个简单的检查和return即可。

DoDeactive()
这个函数当你的view被注销时调用,view被注销时通常有两种情况:一是你的应用程序要退出了,二是相同程序里另一个view要被激活。这个函数很重要,咱们可不能忘记了:)

HandleForegroundEventL()
这个函数主要是在你的view被激活时调用(即在DoActivateL()和DoDeactivate()之间被调用)。当你的view在前台时,它将是HanleForegroundEvent(ETrue),当你的view移出前台时,它将为HandleForegroundEvent(EFalse),只有当前台状态确实改变时这个函数才会被调用。

HandleCommandL()
当view菜单产生一个命令后本函数将被调用。

HandleStatusPaneSizeChange()
当client的尺寸由于status pane而改变时,本函数被调用了就。

下面是一个view在接受事件的典型调用顺序
1.DoActivateL()
2.HandleForegroundEventL(ETrue)
3.HandleForegroundEventL(EFalse)
4.DoDeactivate()

其中一对HandleForegrounEventL在view被激活的过程中会发生多次调用。

而DoActivateL()在DoDeactivate()被调用前可能会被多次调用。

View Resources
如果你要使用view来显示pages,那唯一的途径是创建出自己的AVKON_VIEW资源,里面有自己的CBA和菜单,把这个资源的id传递给view的BaseContructL()函数即可。

RESOURCE AVKON_VIEW r_viewapp_view1
{
hotkeys = r_viewapp_hotkeys;
menubar = r_viewapp_view1_menubar;
cba = R_AVKON_SOFTKEYS_OPTIONS_BACK;
}
注意如果没有给定的menubar资源,那就会使用确省的系统menubar

Construction
下面这个例程是用的AppUI object's ConstructL来构造出view的,使用了AddView来登记,最后把第一个创建的view做为缺省的view了:

void CMyViewArchAppUi::ConstructL()
{
BaseConstructL();

CMyViewArchAppView1* view1 = new(ELeave) CMyViewArchAppView1;
CleanupStack::PushL(view1);
view1->ConstructL();
AddViewL(view1); //Add view1 to CAknAppUi; transfers ownership
CleanupStack::Pop();

CMyViewArchAppView2* view2 = new(ELeave) CMyViewArchAppView2;
AddViewL(View2); // transfer ownership to CAknAppUi
CleanupStack::Pop();

CMyViewArchAppView3* view3 = new(ELeave) CMyViewArchAppView3;
AddViewL(View2); // transfer ownership to CAknAppUi
CleanupStack::Pop();

SetDefaultViewL(*view1);

.......(more code)

要让view发挥作用(注意,一个view自身是没有绘图能力的),它必须拥有得到一个从CCoeControl派生的containers,譬如
class CMyViewArchAppView1Container: public CCoeControl, MCoeControlObserver

下面这个例程将展现编程者自己的CAknView派生类
class CMyViewArchAppView1: public CAknVIew
{
.....
private:
CMyViewArchAppView1Container* iVIew;
}

大家可以看出App里产生了View,View类中有我们必须的Container.

当前激活的view在HandleCommandL()处理命令,这些是自定义功能键和菜单所生成的命令。

void CMyAppView1::HandleCommandL(TInt aCommand)
{
switch (aCommand)
{
case EMyAppCmdSwitchView:
AppUi()->ActivateLocalViewL(KView2Id);
break;
case EAknSoftKeyOk:
{
......
break;
}
case EAknSoftKeyBack:
{
((MEikCommandObserver*)AppUi())->ProcessCommandL(EEikCmdExit);
break;
}
default:
AppUi()->HandleCommandL(aCommand);
break;
}
}

Local View Switching
如果你要切换view,你得提供view的UID
//Now switch the view to view2
iAvkonViewAppUi->ActivateLocalViewL(TUid::Uid(2));
事实上,每个view都有自己的菜单系统,如果你要使用它,应当在AVKON_VIEW资源结构中设置,前面已经讲过了。

而且不管怎么说,如果要使用系统菜单,它的内容在切换之前是一定要更新一次的。
//Switch to a new menu system for the new view
iEikonEnv->AppUiFactory()->MenuBar()->SetMenuTitleResourceId(R_MY_VIEW_ARCH_APP_VIEW2_MENU);
//Now swtich the view to view2

如果要进行远程的视图切换,那就要调用CCoeAppUi::ActivateViewL()函数,并传递了一个包含目标应用程序UID和目标视图UID的TVwsViewId

Leave recovery
Avkon视图体系结构在DoActivateL()退出时会有个自动的回复机制。系统会调用DoDeactivate()在当前view离去时,回复前一个view,并把用户带到之前他们所在的地方。如果程序并没有前一个view的存在,那它就会退出,如果程序前个view就是当前的view(也就是说他们是重激活的),则应用程序会试图恢复缺省的view.

说了这么多,那我们在什么情况下采用合适的体系结构那?下面的内容将会有所帮助。

使用传统的SymbianOS体系结构,你要花费大量的时间研究代码是怎么工作的,但大部分交互和错误处理的引擎代码都是现成,可以降低你的开发难度。

Do you have an acyclic graph shaped navigation structure?
如果你视图程序中的navigation能够as an acyclic gragh,那最好使用dialog体系结构。

Are all the application screens dialog like?
如果你的应用程序想使用对话框,那建议你使用dialog体系结构,注意如果要有一个选择列表,那最好写成包含在dialog中的选择列表。

Does the application have multiple views or modes, which deal with different sorts of data at the top level?
如果是这样,那最好使用传统或view体系结构。

Do external applications need to switch to different views of your application?
如果是采用view体系结构写的程序,那它一定支持多个不同的view,而且是外部程序能访问的。如果你从头开始写一个应用程序,那建议你使用view体系结构,

否则给我看看有没有现成的代码可利用,不要一味的傻写呵呵。注意,如果一个外部程序使用的显示页面是你的程序所提供的,那你应该在DLL中处理这个显示页,这个DLL应该是外部程序所能访问的。

Can all of the applications views be exited without loosing user data?
使用view体系的程序必须有能力处理因外部程序而引起的views的释放。如果程序不能自动的释放这些view,那就要考虑其他两种体系了,要么使用view体系as a message passing system,要么让app UI在传统体系中管理view。

Do external applications need complex interactions with the data in your app?
如果外部程序和你的程序有大量的数据交互,那你最好选择client/server系统。它将减小view的使用量。大量现有的复杂应用,如短信,web以及通讯录都使用这样的机制,这是个很好的构想,总比要重写他们要好:)

Is there only a single complex main view in the app?
如果是这种情况,那还是使用传统的体系。

<一些例子>
这里给出一些假象例子的解决方案。

App launcher
这种程序可以单独运行并且能切换到其他程序,它只有一个view ,并且不接受外部程序的访问,如果这样会中断它。这样的程序不需要消息的传递或外部程序需要的views.

Fast swap window
一个在运行时刻的弹出窗口,这是一个睡眠的弹出对话框其实,它没有状态。

Email app
电子邮件程序可以读或写电子邮件。这个程序有外部使用views,内部可切换的views,并能做中断操作。Notifier信息能告知程序显示一个新的信息给用户,所以该程序要有处理此请求的机制,即使在做其他事情时。编辑框可以做为其他程序的view显示,使用DLL形式,我们就可以做到外部程序的调用自如。SymbianOS的messaging程序就是一个client/server机制可以在其他程序中被调用。当然要实现内嵌在外部程序中有许多工作要做的。当你在写邮件时,很可能又收到新邮件的同志,这个时候新邮件是做为一个嵌套的对话框似的组件显示在里面的,注意,做这个工作时要特别依赖inter-application interaction model。

Contacts app
通讯录程序允许进行通讯录的显示、编辑和选择。选择后的结果可以做为其他程序所用。注意它不能因外部view的切换而中断。

通过一个外部程序可访问的dll,通讯录就可以被外部程序所访问。程序本身并不需要views,所以最好使用传统的体系结构编写,而用dialog应付所有的编辑窗口。

web browser
web浏览器可以做为单一的web上网工具,也可以内嵌在其他的文档中。web浏览器可以通过外部程序可以访问的dll来提供一个UI control,但并不给外部提供可用的全屏应用。它必须能够应付外界的访问,譬如说应付在点击mail连接后撰写电子邮件的过程。
主体程序主要是用传统的体系结构,也可以采用view体系来应付访问页面的需求。

Settings
我们要关心的是全局设置和特定应用程序的设置。他们不是一回事情。经管他们可能共享一个库,实际上他们互不相干。

设置程序并不需要为外部的访问做什么处理提供什么界面,因此他的编写很简单,用传统的或者基于dialog的体系都可以。而局部设置要注意必须小心对待外部可能出现的中断,并且他有可能做为一个模态dialog而处理。

Telephony app
电话号码簿程序并不提供外部view,但他必须要处理外部资源的请求。有时候直接用传统的体系去写即可,大部分的外部交互可以通过ETEL Server.

[应用程序的启动]
从CEikApplication派生的类CAknApplication,他有几个必须重载的函数:
PreDocContructL()
OpenIniFileLC(RFs& aFs)

PreDocConstructL,主要是处理已经构造好的应用程序实体是否正确准备好了。如果已经准备好了,那应用程序可以切换到实体上。注意,这个函数只检查非内嵌程序。

通常,ini文件并不被series60所支持,如果要处理,那就要强制性的调用CEikApplication::OpenIniFileC

[BASE CLASSES]

CAknDocument
这个类是做为应用程序文档的基类准备的。用这个函数访问文档可以不用初始化。这个是访问Avkon应用程序的比较好的途径。

CAknAppUi
所有的Avkon应用程序必须从这个类派生。
这个类支持下面几个特定应用函数:
KeySound support
Accessories for CBA and StatusPane
TextResolver-Avkon-specific error reporting from CAknAppUI::HandleError()
Avkon view architectrue integration
Control dumping - Debug feature

CAknViewAppUi
所有的视图结构必须由此派生。