以下转自Nokia Symbian手册汇编:
资源文件
指定了应用程序的相关信息,包含要显示的控件定义(如菜单,对话框等),是所有应用程序所必须的。
资源文件的后缀名为<application name>.rss。最多可以包含4095个资源。
一个应用程序可以包含多个资源文件,并在运行时动态加载其他资源文件。
Uikon预先定义了许多在资源文件中可以使用的结构体。
资源文件需要编译成二进制文件,在C++代码中需要include编译产生的.rsg文件。
具体的文件及解释如下:
// 4 letter ID 该字段必须每个应用程序唯一
NAME S60R
// INCLUDES 包含已经定义的资源结构
#include <eikon.rh>
#include <avkon.rh>
#include <avkon.rsg>
#include <appinfo.rh>
//hrh用于包含枚举常量,如菜单项;rls定义了在UI中使用的字符串
#include "S60ResourceLab.hrh"
#include "S60ResourceLab.rls"
//允许应用程序指定版本号,可选
RESOURCE RSS_SIGNATURE
{
}
//为该应用程序的默认文件指定一个名字
RESOURCE TBUF r_default_document_name
{
buf="HEWB";
}
//指定了所要显示的菜单和soft keys
RESOURCE EIK_APP_INFO
{
menubar=r_s60resourcelab_menubar;
cba=R_AVKON_SOFTKEYS_OPTIONS_BACK;
}
菜单相关:
//定义了需要显示的menu pane,txt字段为空(S80中才有位置显示菜单的title,S60没有)
RESOURCE MENU_BAR r_s60resourcelab_menubar
{
titles=
{
MENU_TITLE { menu_pane=r_s60resourcelab_menu; txt=""; }
};
}
//menu pane定义了一系列menu items,command就是HandleCommandL中接收的命令参数,txt显示给用户。
RESOURCE MENU_PANE r_s60resourcelab_menu
{
items=
{
MENU_ITEM { command=EAknCmdExit; txt="Exit"; },
MENU_ITEM { command=ES60ResourceLabCmdAppTest; txt="Test"; }
};
}
字符串相关:字符串的内容真正定义的位置是在本地化文件中,见后。
RESOURCE TBUF r_s60resourcelab_text_goodbye
{
buf = qtn_s60resourcelab_text_goodbye;
}
RESOURCE TBUF r_s60resourcelab_text_everyone
{
buf = qtn_s60resourcelab_text_everyone;
}
Localisation Files 每种显示给用于的语言都有一个本地化文件,在该文件中包含所有需要翻译的语言。
可以在rss文件中被条件包含,如下所示:
#ifdef LANGUAGE_01
#include "HelloWorld01.rls"
#elif defined LANGUAGE_02
#include "HelloWorld02.rls"
#endif
示例:keyword symbolic_identifier the_string
rls_string qtn_caption_string "S60 Resource Lab"
rls_string qtn_s60resourcelab_text_goodbye "Goodbye"
Resource Compilation 资源编译器将rss文件编译为rsc文件,中间产生一个rsg的头文件。
C++代码中使用resource需要包含rsg头文件,使用方法如下:
HBufC* textResource = StringLoader::LoadLC( R_COMMAND1_TEXT );
iLabel->SetTextL(*textResource);
CleanupStack::PopAndDestroy( textResource );
读后感Symbian的资源对于初学者来说也是一个很棘手的问题。
往往添加一个menu item需要同时改很多地方,稍不小心就N多错误,让人摸不着头脑。
但是熟悉之后,一切都显得那么自然,也许这就叫熟能生巧吧,呵呵。
peter (2007-11-04 16:07:01)
Basic Application Structure 一般应用程序都分为UI和Engine两部分。
UI负责显示和接受用户命令
Engine负责处理数据,可以被其他应用程序重用
Basic Application Classes 下图展示了一个最基本的应用程序类图,其中每部分的作用如下:
Application:返回UID3,创建Document类
Document:主要创建Application UI,也可以从文件中读写状态数据
App UI:主要的用户界面,本身不可见,但是拥有可见控件,主要处理菜单命令和按键事件
View/Container: 包含用户可见的控件,一个应用程序中可能有多个view或container
Model:根据应用程序情况的不同,可能被Document,AppUI或者View所拥有
Class Derivations 下图展示了一个CMyApp应用程序的类继承关系,总共有四层:
最上层:CMyApp类,用户应用程序相关类
Avkon层:CAkn类与S60应用程序框架相关的类
Uikon层:CEik类勾勒出应用程序框架,在所有的UI设计中(如S60,UIQ,FOMA等)都存在
AppArc层:CApa类定义了最基本的应用程序接口
CONE层:CCoe类定义了最基本的可见控件(CCoeControl),底层的异步事件处理(其他两个)。
Startup Sequence 下图解释了应用程序框架类的创建顺序,当应用程序开始时,首先调用的是E32Main()函数,由它调用应用程序框架来产生应用程序对象。具体过程如图所示,可以对照一个HelloWorld GUI程序看看:
需要注意的地方:最后一步,只有当CCoeControl::ActivateL()被调用并且控制返回到系统的Active Scheduler(应用程序事件循环),Container的Draw()才会被调用,即控件才会显示出来。
Appcliation Entry Point Symbian中的每个应用程序都运行在其自己的进程中,必须实现以下两个全局函数(通常向导已经帮你生成了):
E32Main():应用程序主入口,调用系统框架函数EikStart::RunApplication()来初始化和运行应用程序
NewApplication():应用程序进程创建一个应用程序实例,该实例必须继承自CApaApplication
以上信息专门针对Symbian 9.x,在以前的版本中,应用程序是作为一个多态的Dll实现的。
下面详细介绍一个应用程序框架中各个主要的类
Application Class 当应用程序启动后第一个产生的类,继承自CAknApplication,它定义了一个应用程序最基本的行为。
class CMyAppApplication : public CAknApplication
{
public: // Functions from base classes
TUid AppDllUid() const;
protected: // Functions from base classes
CApaDocument* CreateDocumentL();
};
该类主要有两个功能:
首先返回每个应用程序唯一的UID3
然后产生一个具体的document对象
Document
Document类一般用来将应用程序的数据保存到文件中,保持程序持久性。但是默认情况下Symbian不使用文件。
class CMyAppDocument : public CAknDocument
{
public: // Constructors and destructor
static CMyAppDocument* NewL( CEikApplication& aApp );
static CMyAppDocument* NewLC( CEikApplication& aApp );
virtual ~CMyAppDocument();
public: // Functions from base classes
CEikAppUi* CreateAppUiL();
private: // Constructors
void ConstructL();
CMyAppDocument( CEikApplication& aApp );
};
不使用文件,该类主要有两个功能:
二阶段构造,创建一个Document对象:外部接口为NewL,默认构造函数和二阶段构造函数都为private。
创建一个AppUi
App UI 作为应用程序用户接口,它创建并拥有用于显示用户数据的控件,但是本身不负责显示,主要用来处理菜单命令和按键事件。通过实现以下虚函数,可以处理相应事件:
HandleKeyEventL():按键事件
HandleForgroundEventL():应用程序被切换到前台
HandleSwitchOnEventL():手机开机事件
HandleSystemEventL():系统事件
HandleWsEvent():窗口服务器事件
HandleApplicationSpecificEventL():应用程序相关的事件
HandleCommandL():处理定义在文件中的命令
class CMyAppAppUi : public CAknAppUi
{
public: // Constructors and destructor
void ConstructL();
CMyAppAppUi();
virtual ~CMyAppAppUi();
private: // Functions from base classes
void DynInitMenuPaneL(TInt aResourceId, CEikMenuPane* aMenuPane);
void TKeyResponse HandleKeyEventL(const TKeyEvent& aKeyEvent, TEventCode& a Type);
void HandleCommandL( TInt aCommand );
void HandleStatusPaneSizeChange();
private: // Data
CMyAppAppView* iAppView;
};
从定义中可以看到这里并没有二阶段构造(NewL,NewLC) ,因为第一阶段构造和第二阶段构造都是应用程序框架调用的:CMyAppDocument::CreateAppUiL(),CEikAppUi::ConstructL()。
CMyAppAppUi::DynInitMenuPaneL() 显示菜单之前由应用程序框架调用,以便动态控制菜单显示的内容。
CMyAppAppUi::HandleKeyEventL() 当有按键事件发生并没有被其他控件处理时,系统调用此函数。
CMyAppAppUi::HandleCommandL() 当用户按下菜单命令时调用,如何定义一个菜单命令在下一讲中详述。
AppUi生成Container:根据S60的惯例,所有控件都成为container,至少有一个container在AppUi的第二阶段构造中生成。其中有以下几个主要函数:
SetMopParent() 使得container可以访问AppUi,以便显示滚动条等
AddToStack() 将container添加到App UI的控制堆栈,以便接收按键事件
Container Class 一个container可以包含任意多个控件。
class CMyAppAppView : public CCoeControl
{
public: // New methods
static CMyAppAppView* NewL( const TRect& aRect );
static CMyAppAppView* NewLC( const TRect& aRect );
virtual ~CMyAppAppView();
public: // Functions from base classes
void Draw( const TRect& aRect ) const;
virtual void SizeChanged();
TInt CountComponentControls() const;
CCoeControl* ComponentControl(TInt aIndex) const;
private: // Constructors
void ConstructL(const TRect& aRect);
CMyAppAppView();
private: //data
CEikLabel* iLabel;
};
主要函数解释:
SizeChanged() 当container被创建或者大小发生变化时调用
CountComponentControls() 返回该container所包含的控件数量
ComponentControl() 返回该container所包含的控件的指针
Draw() 执行一些该container相关的显示操作
所有container的初始化操作在第二阶段构造函数ConstructL()中完成。
void CMyAppAppView::ConstructL( const TRect& aRect )
{
CreateWindowL();
iLabel = new (ELeave) CEikLabel;
iLabel->SetContainerWindowL(*this);
iLabel->SetTextL( _L("Hello World") );
SetRect(aRect);
ActivateL();
}
CreateWindowL() 创建该container的一个窗口,以便这个container和它的控件都能显示出来。
Lable通过三个步骤创建:首先调用默认构造函数、添加到当前window中、设置要显示的字符串。
SetRect() 设置container的返回,如果Label超出此范围,会被裁减掉
ActivateL() 最后container发出已经准备好显示的信号,可以执行Draw操作
读后感初学者可以大体浏览一下,因为这是每个应用程序都具备的
当出现应用程序一开始就死掉或者想对应用程序有更好的控制时,须仔细阅读
当熟练掌握本章内容时,当需要添加一个功能时,可以很快找到需要添加的位置