Symbain OS 对话框的基类实在Uikon中定义的CEikDialog类,在此基础上各个界面平台又封装了各种对话框类,例如S60平台的Avkon定义了CAknDialog类作为S60对话框的基类,而UIQ平台仍然使用CEikDialog类作为对话框基类,但是封装了CQikSaveFileInFolderDlg类等UIQ平台特有的对话框类。
Uikon中的CEikDialog实现了三个接口,包括:
McoeControlObserver,对话框子控件的观察者接口,当子控件状态发生变化时该接口的HandleControlEventL()函数被调用。
MeikDialogPageObserver,在多页对话框中,可以通过实现该接口观察页面的变化。
McoeControlContext,在多个控件之间共享图形上下文的接口。
CAknDialog类和CEikDialog类最大的不同是前者可以拥有菜单,相应地实现了MEikMenuObserver接口,用于观察菜单状态的变化。
CAknDialog是大部分Series60对话框的基类。关于对话框的使用,大体分为以下步骤:
1.定义资源。2.执行对话框。3.动态地初始化标准对话框。4.退出对话框。
1.定义资源
使用DIALOG定义资源,资源指定了对话框的布局,在资源中定义对话框应该包含的行数、使用的控件、对话框是否为模态以及使用的软键等。资源结构在eikon.h然后中定义:
STRUCT DIALOG
{
LONG flags=0;
LTEXT title="";
LLINK pages=0;
LLINK buttons=0;
STRUCT item[];
LLINK form=0;
}
flags:说明了对话框的属性,其中的属性值可参考<uikon.hrh>,可以多个一起用。
#define EEikDialogFlagWait //等待对话框
#define EEikDialogFlagNotifyEsc //退出通知,当用户选择某个软键时对话框调用OkToExit()函数
#define EEikDialogFlagButtonsBelow
#define EEikDialogFlagButtonsRight
#define EEikDialogFlagNoUserExit //不允许用户选择退出对话框,即不显示软键
#define EEikDialogFlagModeless //无模式对话框,使用该
#define EEikDialogFlagNoTitleBar //无标题栏
#define EEikDialogFlagAllKeysToButtons
#define EEikDialogFlagFillScreen
#define EEikDialogFlagNoDrag //不可拖动
#define EEikDialogFlagDensePacking
#define EEikDialogFlagNoBackup
#define EEikDialogFlagFillAppClientRect //对话框占满整个主面板
#define EEikDialogFlagCbaButtons //使用CBA按钮
#define EEikDialogFlagNoBorder
#define EEikDialogFlagNoShadow
其中,把标准对话框定义为等待对话框是一种良好的习惯。应该只在合理的时候(例如在主应用程序窗口中)才把标准对话框定义为非等待。
title:对话框的标题栏文本
pages:多页对话框中,页面资源的ID
buttons:指定了使用的软键(按钮资源的ID),其中的取值可参考
#define R_AVKON_SOFTKEYS_EMPTY
#define R_AVKON_SOFTKEYS_EMPTY_WITH_IDS
#define R_AVKON_SOFTKEYS_OK_EMPTY
#define R_AVKON_SOFTKEYS_SELECT_CANCEL
#define R_AVKON_SOFTKEYS_OK_CANCEL
#define R_AVKON_SOFTKEYS_OK_DETAILS
#define R_AVKON_SOFTKEYS_CALL_CANCEL
#define R_AVKON_SOFTKEYS_OPTIONS_BACK
#define R_AVKON_SOFTKEYS_OPTIONS_DONE
#define R_AVKON_SOFTKEYS_OPTIONS_CANCEL
#define R_AVKON_SOFTKEYS_OPTIONS_EXIT
#define R_AVKON_SOFTKEYS_OK_BACK
#define R_AVKON_SOFTKEYS_CANCEL
#define R_AVKON_SOFTKEYS_BACK
#define R_AVKON_SOFTKEYS_CLOSE
#define R_AVKON_SOFTKEYS_DONE_BACK
#define R_AVKON_SOFTKEYS_DONE_CANCEL
#define R_AVKON_SOFTKEYS_SELECT_BACK
#define R_AVKON_SOFTKEYS_MARK_BACK
#define R_AVKON_SOFTKEYS_UNMARK_BACK
#define R_AVKON_SOFTKEYS_YES_NO
#define R_AVKON_SOFTKEYS_UNLOCK_EMPTY
#define R_AVKON_SOFTKEYS_SAVE_BACK
#define R_AVKON_SOFTKEYS_SHOW_CANCEL
#define R_AVKON_SOFTKEYS_SHOW_EXIT
#define R_AVKON_SOFTKEYS_ANSWER_EXIT
#define R_AVKON_SOFTKEYS_EXIT
#define R_AVKON_SOFTKEYS_READ_EXIT
#define R_AVKON_SOFTKEYS_LISTEN_EXIT
#define R_AVKON_SOFTKEYS_SEARCH_BACK
#define R_AVKON_SOFTKEYS_AGAIN_QUIT
#define R_AVKON_SOFTKEYS_QUIT
#define R_AVKON_SOFTKEYS_INSERT_BACK
items:它定义了对话框包含的实际内容。需要为希望包含到对话框的每个控件分别定义一个DLG_LINE,它也在eikon.rh总定义,在每个DLG_LINE中至少应该指定以下字段:
* id :在应用程序中使用该值引用此对话框行,必须在应用程序的.hrh文件中枚举它。
* type :此对话框行包含的控件类型(在avkon.hrh或uikon.hrh中定义)。
* control :在此对话框中使用的控件。
此外,还可以选择在DLG_LINE资源中指定一个itemflags字段,使用该字段决定对话框行的行为。例如弹出字段文本控件可以使用 itemflags说明应该在“选择”键被按下时打开一个弹出窗口,例如 EEikDlgItemTakesEnterKey|EEikDlgItemOfferAllHotKeys。可以在uikon.hrh中找到 itemflags的值。最常用的可能当属EEikDlgItemSeparatorBefore标志,用于在对话框行之前插入一个水平“分隔符”行。
本节的MySimpleDlg示例程序中定义了一个显示一个标签的简单的对话框,在.rss中定义的资源如下:
RESOURCE DIALOG r_simpledlg_dialog
{
flags=EEikDialogFlagNoDrag|EEikDialogFlagNoTitleBar|EEikDialogFlagFillAppClientRect|
EEikDialogFlagCbaButtons|EEikDialogFlagWait|EEikDialogFlagNotifyEsc;
buttons = R_AVKON_SOFTKEYS_OK_CANCEL;
items =
{
DLG_LINE
{
id = ESimpleDlgCtlLabel; //子控件的ID(在下面.hrh中定义)
type = EEikCtLabel; //子控件的类型
control = LABEL //子控件的资源定义
{
txt = "This is a simple dialog";
};
}
};
}
除此之外还要在.hrh中定义:
enum TSimpleDlgControlIds
{
ESimpleDlgCtlLabel = 1
};
2.执行对话框
在c++代码中一般用CEikDialog::ExecuteLD()函数执行对话框,该函数在eikdialg.h中定义:
//eikdiag.h
...
public:
virtual TInt ExecuteLD(TInt aResourceId);
....
函数的参数aResourceId是执行对话框的资源ID,如果对话框的属性值包含EEikDialogFlagWait,则表示是一个等待对话框,此时ExecuteLD()函数调用会挂起,知道对话框关闭后该函数才返回。对于非等待对话框,该函数会立即返回。 对话框在ExecuteLD()的最后会删除自己。
本节MySimpleDlg程序中创建了一个CAknDialog对象,并调用它的ExecuteLD()函数,代码如下
//MySimpleDlgAppUi.cpp
#include<akndialog.h>
....
void CMySimpleDlgAppUi::HandleCommandL(TInt aCommand)
{
switch ( aCommand )
{
...
case ESimpleDlgCmdDialog: //在下面的.rss和.hrh中定义
{
iEikonEnv->InfoMsg(_L("test"));
CAknDialog* dialog = new(ELeave)CAknDialog; //创建对话框对象
if(dialog->ExecuteLD(R_SIMPLEDLG_DIALOG)) //执行对话框并判断返回值
{
_LIT(KButtonOk,"OK");
iEikonEnv->InfoMsg(KButtonOk());
}
else
{
_LIT(KButtonCancel,"Cancel");
iEikonEnv->InfoMsg(KButtonCancel());
}
break;
}
........
// TODO: Add Your command handling code here
default:
break;
}
}
那么为什么在new之后没有将其压入清除栈呢?因为ExecuteLD()拥有改对话框的所有权,该方法包装了对其他两个方法的调用:PrepareLC()和RunLD()。PrepareLC()会把对话框的指针放入清除栈,然后完成对话框的创建。RunLD()会显示该对话框,然后把它从清除栈中弹出。但如果在调用ExecuteLD()之前需要调用任意可能导致异常退出的代码,则应该将其放入清除栈,并在调用 ExecuteLD()前把它弹出。
在.rss和.hrh文件中定义ESimpleDlgCmdDialog:
//MySimpleDlg.rss
RESOURCE MENU_PANE r_mysimpledlg_menu
{
items =
{
MENU_ITEM { command = ESimpleDlgCmdDialog; txt = qtn_appl_dialog; },
MENU_ITEM { command = ESimpleDlgCmdPrepare; txt = qtn_appl_prepare; },
MENU_ITEM { command = ESimpleDlgCmdPreLayout; txt = qtn_appl_prelayout; },
MENU_ITEM { command = EAknCmdExit; txt = qtn_appl_exit; }
};
}
//MySimpleDlg.hrh
enum TMySimpleDlgCommandIds
{
ESimpleDlgCmdDialog = 1,
ESimpleDlgCmdPrepare,
ESimpleDlgCmdPreLayout
};
3.动态地初始化标准对话框
对话框的子控件在定义时被赋予初始值,但在某些情况下应用程序需要在执行对话框之前根据情况改变子控件的值,有两种方法:
第一种:使用PrepareLC()函数和RunLD()函数
ExecuteLD()函数内部首先调用PrepareLC()函数从资源中加载对话框,然后调用RunLD()函数弹出对话框,因此可以在这两个函数之间加入初始化对话框的代码,本节MySimpleDlg示例程序MySimpleDlgAppUi.cpp文件中代码如下:
#include <eiklabel.h>
void CMySimpleDlgAppUi::HandleCommandL(TInt aCommand)
{
switch ( aCommand )
{
......
case ESimpleDlgCmdPrepare:
{
CAknDialog* dialog = new(ELeave)CAknDialog;
dialog->PrepareLC(R_SIMPLEDLG_DIALOG); //从资源文件中加载对话框
CEikLabel* label = static_cast<CEikLabel*>(dialog->Control(ESimpleDlgCtlLabel)); //获取 对话框 的子控件的指针
_LIT(KMyLabel,"Changed after PrepareLC()");
label->SetTextL(KMyLabel()); //修改子控件的值
dialog->RunLD();
break;
}
.......
// TODO: Add Your command handling code here
default:
break;
}
}
注意:需要在.mmp文件中加入eikdlg.lib
第二种方法:使用PreLayoutDynInitL()函数或PostLayoutDynInitL()函数
该方法需要自定义对话框类,并重载CEikDialog::PreLayoutDynInit()函数和CEikDialog::PostLayoutDynInitL()函数。这两个函数都是虚函数,他们会在显示对话框之前被调用。其中对话框完成控件布局会调用PreLayoutDynInitL()函数,因此该函数常用于完成可能会影响对话框布局的初始化操作,例如创建新的控件。对话框完成布局之后会调用PostLayoutDynInitL()函数,该函数用于实现不影响对话框布局的操作,如为控件赋值等。
本节的MySimpleDlg示例程序自定义了一个对话框CMySimpleDialog类,它重载了PreLayoutDynInitL()函数,MySimpleDialog.cpp主要代码如下:
#include <eikenv.h> // for CEikonEnv
#include <eiklabel.h> // for example label control
#include "MySimpleDlg.hrh"
void CMySimpleDialog::PreLayoutDynInitL()
{
CEikLabel* label = static_cast<CEikLabel*>( dialog->Control(ESimpleDlgCtlLabel)); //通过CEikDialog::Control()函数获得控件的指针
_LIT(KMyLabel, "Changed by PreLayoutDynInitL()");
label->SetTextL(KMyLabel());
}
使用此方法可以直接调用ExecuteLD()函数执行对话框,MySimpleDlgAppUi.cpp代码如下:
#include <eiklabel.h> // for CEikLabel
#include <avkon.hrh>
#include <akndialog.h> //对话框头文件
#include "MySimpleDialog.h" // for CMySimpleDialog
.....
void CMySimpleDlgAppUi::HandleCommandL(TInt aCommand)
{
switch ( aCommand )
{
......
case ESimpleDlgCmdPreLayout:
{
CAknDialog* dialog = new(ELeave)CMySimpleDialog; //创建自定义对话框
dialog->ExecuteLD(R_SIMPLEDLG_DIALOG); //执行对话框
break;
}
// TODO: Add Your command handling code here
default:
break;
}
}
注意:加入新类以后,最好把.h头文件放在inc文件夹中,.cpp源文件放在src文件夹中(保持统一),必须在.mmp文件中加入source文件,如本章的.mmp加入如下代码:
SOURCE MySimpleDialog.cpp
最后重新编译。
4.退出对话框
当为对话框资源指定了EEikDialogFlagNotifyEsc标记时,对话框会在用户按下软键时调用CEikDialog::OkToExitL()函数。因此可以在自定义的对话框中重载CEikDialog::OkToExitL()函数。当该函数返回ETrue时,对话框退出,并且由系统销毁。返回EFalse时,对话框继续保留。
本节的MySimpleDlg示例程序中的自定义对话框CMySimpleDialog类,该类重载了此函数,代码如下:
#include <eikenv.h> // for CEikonEnv
TBool CMySimpleDialog::OkToExitL(TInt aButtonId)
{
//调用基类的OkTkExitL()函数,完成默认功能,例如弹出菜单等。
TBool ret = CAknDialog::OkToExitL(aButtonId);
CEikonEnv* env = CEikonEnv::Static();
if(aButtonId==EEikBidOk) //用户选择了左软键
{
_LIT(KOkToExit, "OK to exit");
env->InfoMsg(KOkToExit());
}
else if(aButtonId==EEikBidCancel) //用户选择了右软键
{
_LIT(KCancelToExit, "Cancel to exit");
env->InfoMsg(KCancelToExit());
}
return ret; //返回CAknDialog::OkToExitL()函数的返回值
}