伯乐共勉

讨论。NET专区
  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

HelloWorldPlus分析

Posted on 2007-02-09 17:54  伯乐共勉  阅读(467)  评论(0编辑  收藏  举报
 对于HelloWorldPlus的分析可以从对象构建一步步深入,它和HelloWorldBasic略有不同之处。

一、AppUi的 ConstructL() 方法

void CHelloWorldPlusAppUi::ConstructL()
 {
   // Initialise app UI with standard value.
   // Flag 'EAknEnableSkin' indicating that default skin parameters should be provided
   // by UI controls created within the scope of this AppUi instance.
   // 用一个标准的值初始化APP UI 
  // EAknEnableSkin 是CCoeAppUi的一个枚举值。它指定默认的皮肤参数必须由
  // 在这个AppUi实例作用范围内创建的组件提供
   BaseConstructL( EAknEnableSkin );

   // Create view object
  // 创建一个视图对象
   iAppView = CHelloWorldPlusAppView::NewL( ClientRect() );

   // Add to control stack in order to get key presses.
  // 把视图对象放入到 控件栈,以便获得键盘事件。
   AddToStackL( iAppView );
 }

    在这个函数中,首先调用了 BaseConstructL()方法,使用一个标准值初始化AppUi对象。然后调用自身的静态构造方法NewL()方法构 造自身,这里需要关注的是它的参数 ClientRect() 方法,虽然这个函数再这里似乎没有什么实际的用处,但是还是需要了解。 AddToStackL()方法则把AppView对象放置到控件栈中,以便获取键盘事件。于是我们要问,什么是控件栈 control stack,它 是做什么用的?

[1] CEikAppUi 的 BaseConstructL() 方法

protected: void BaseConstructL(TInt aAppUiFlags=0);

   使用一个标准的值来初始化App Ui对象,这个时候应用程序的标准的资源文件被读取,除非这个函数被传入了ENoAppResourceFile或 ENonStandardResourceFile。这个函数默认参数值是0,它指定应用程序用户接口标志。

[2] CEikAppUi 的 ClientRect() 方法

TRect ClientRect() const;

    获得可被应用程序获得用于绘制的屏幕区域,但是不包括以下这些区域:非应用程序区域,那些地方一直显示几乎不变的内容、应用程序状态栏、应用程序按钮组、应用程序菜单栏、应用程序标题栏和工具栏。

    这里需要重视的是,矩形的直角坐标系是和整个屏幕关联的,例如矩形的左上角点的坐标值可能是(0,45)。这里的意思是,这个获得的矩形在屏幕中的位置是 由一个和屏幕对应的直角坐标系确定的,显然这个坐标系的(0,0)点位于屏幕的左上角,x轴正向向下,y轴正向向右。

[3] CEikAppUi 的 AddToStackL()方法

void AddToStackL(CCoeControl* aControl,TInt aPriority=ECoeStackPriorityDefault,TInt aStackingFlags=ECoeStackFlagStandard);

   把控件放到控件栈中,这个函数经常被GUI应用程序使用,把应用程序视图放置到控制栈中。

   那么,什么是控制栈呢? 一个UI控件同过把自身压入控制栈注册到键盘事件。典型的只有视图对象会把自己加入到控制栈中,然后视图对象通过OfferKeyEventL()函数再分配键盘事件给它的组件控件。

   键盘事件被提供给控件栈上的控件,最后一个压入控制栈的控件通过 OfferKeyEventL()方法首先获得键盘事件,如果这个控件不对这个键盘事件做出响应,那么返回EKeyWasNotConsumed,然后键盘事件被传递到控件栈的下一个控件。

   AppUi通常是控件栈的最底下的一个,所以它也是最后一个获得键盘事件的,如果在它前面的控件都多这个键盘事件不赶兴趣那就抡到AppUi去作出响应了。

   AddToStackL() 方法有几个参数可以看一下:

CCoeControl* aControl 
将要被压入控制栈的控件;

TInt aPriority=ECoeStackPriorityDefault 

控件的栈有限权,这个参数以及其他一些优先权值都是通过一个匿名的枚举类型定义的。

Tint aStackingFlags=ECoeStackFlagStandard 

控件的事件处理行为,这个参数和上面一个参数一样,也是在一个匿名的枚举类型中定义。

在上文中提到了OfferKeyEventL()方法,这个方法是CCoeControl的一个成员,关于这个函数的相关知识在下文中再做介绍。

二 CCoeControl 类,以及 Draw()方法

HelloWorldPlus 是一个以 EIKON 为模板(图形框架)的应用,它的视图类从 CCoeControl 类继承。

class CHelloWorldPlusAppView : public CCoeControl
 {
  ……
  ……
 }

draw()方法有如下代码:

// Draw 是一个CCoeControl 类的一个虚函数
void CHelloWorldPlusAppView::Draw( const TRect& /*aRect*/ ) const
 {

   // Get the standard graphics context
  // 获得图象环境,用于绘制图形
  // CWindowGc 是一个窗口图形环境类
   CWindowGc& gc = SystemGc();

   // 以默认构造函数构造一个矩形
  // 这个矩形包括了屏幕的全部面积
  TRect rect = Rect();

   // Clears the screen
  // 清楚屏幕(清楚矩形所包含的窗口)
   gc.Clear( rect );

   // Query what the current time is
  // 获取系统当前时间
   TTime currentTime;
   currentTime.HomeTime();

   // Format the time into a descriptor, and handle any errors
   TBuf<32> timeAsText;
   _LIT ( KTimeFormat, "%H : %T : %S" );

   // Load a string from the resource file.
  // 从资源文件加载字符串
  // 当前使用的LoadLC是这个函数的第一个重载版本,传入一个资源 ID
   HBufC* textResource = StringLoader::LoadLC( R_HEWP_TIME_FORMAT_ERROR );

   // 格式化时间文本
  TRAPD ( err, currentTime.FormatL( timeAsText, KTimeFormat ) );
   // 如果格式化时间文本出错,那么把错误信息赋给时间文本
  if ( err != KErrNone )
 {
 timeAsText = *textResource;
 }

   // Write the time to the display
  // 在屏幕上输出
  // iCoeEnv 是 CCoeEnv 型的一个CCoeControl的一个数据成员。
   gc.UseFont( iCoeEnv->NormalFont() );
  // 绘制无边框的水平放置的字体
  // 如果该函数调用时,字体未设置,那么将发生Panic
   gc.DrawText( timeAsText, TPoint( 55,60 ) );

  gc.DrawText( timeAsText, TPoint( 55,100 ) );

   // Pop HBuf from CleanUpStack and Destroy it.
   CleanupStack::PopAndDestroy( textResource );
 }

[1] CCoeControl 类 

控件的基类,所有控件类都从这个类继承。

[2] CCoeControl 类的SystemGc() 方法

protected: CWindowGc& SystemGc() const;

获得标准的图形环境,以便在绘制控件时使用。

所有的图形函数都是通过图形环境实现的。

在 图形环境被绘制前,它必须已经被激活;当它不再被需要的时候,图形环境应该处于一种非活动状态。当使用 Draw(), DrawNow() 或  DrawDeferred()完成图形环境绘制,应用程序其实不需要去做这些事情,图形绘制在控件框架中被完成 。对于应用程序初始化的绘制工作来说, 程序并没有使用DrawNow() 或DrawDeferred()这些函数,应用程序只需要使用ActivateGc() 方法激活图形环境或使用  DeactivateGc() 方法使图形环境处于非活动状态。

SystemGc() 方法返回一个 CWindowG 型图形环境引用。

在CHelloWorldPlusAppView类的Draw方法中个使用了SystemGc() 方法:

CWindowGc& gc = SystemGc();

[3] CWindowGc 类

窗口图形环境

绝大多数的窗口环境绘制函数都映射到等价的 CFbsBitGc 类函数——他们作用于任意屏幕上与窗口左上角关联的直角坐标系。

总体来说, 如果服务端函数遇到某些可以使之发生异常退出的情形时,并不异常退出,而是返回一个错误值,以说明函数发生异常退出的情况。这样,leave可以在client/server分界点的适当的一侧被处理。

[4] CWindowGc 类的 UseFont()

virtual void UseFont(const CFont *aFont);

设置环境(上下文)字体

字体是用于文本绘制的。如果字体已经位于字体位图服务器的内存中,那么GDI(图形设备接口,Graphic Device Interface)就会共享那个拷贝。

GDI 可以在不同的上下文中实现纯抽象组件。在它的底部定义了绘图基本元素以及实现与设备无管绘图的一些。

UseFont()函数必须在文本绘制之前调用,否则主调线程就会发生Panic。

该函数需要一个CFont型参数,它是一个设备字体。

在 HelloWorldPlus中,使用iCoeEnv->NormalFont()来获得设备字体。

[5] 语句:iCoeEnv->NormalFont()

iCoeEnv 是 CCoeEnv 型的一个CCoeControl的一个数据成员。CCoeEnv、CCoeControl和CCoeAppUi一起组成了控件环境CONE(Component Enviroment)。
NormalFont()这个函数的作用是获取标准的环境字体。这个字体在控件环境CONE初始化时被创建。

三、AppView 的 ConstructL() 方法

void CHelloWorldPlusAppView::ConstructL( const TRect& aRect )
 {
   // Create a window for this application view
  // 创建一个自己 App View 的窗口
   CreateWindowL();

   // Set the windows size
  // 设置自身窗口的尺寸,该函数的调用将导致 SizeChangedL()函数被调用
   SetRect( aRect );

   // Activate the window, which makes it ready to be drawn
  // 激活这个窗口,使可被绘制
   ActivateL();
 }

[1] CCoeControl 的 CreateWindowL()

protected: void CreateWindowL();

创建一个控件的窗口,被创建的窗口属于应用程序窗口组的子窗口。这个函数使一个指定控件成为一个窗口拥有的控件,并且通常在控件的 ConstructL()方法中被调用。

四、关于OfferKeyEventL()方法

// CHelloWorldPlusAppView::OfferKeyEventL()
// Handles keyevents.
// ---------------------------------------------------------------------------
// 对键盘事件的处理
// TKeyEvent键盘事件类。它描述了键盘事件的细节
// 说明事件的类型,是Press,keydown还是 keyup 或是其它
TKeyResponse CHelloWorldPlusAppView::OfferKeyEventL( 
 const TKeyEvent& aKeyEvent,TEventCode aType )
 {

 // We only want the key press, not the key up/down event
 if ( aType == EEventKey )
 {
 // Check if the 2 key was pressed
 if ( aKeyEvent.iCode == KDeviceKeyTwo )
 {

 // Load a string from the resource file.
 HBufC* textResource = StringLoader::LoadLC( R_HEWP_KEY_TWO_PRESSED );

 CAknInformationNote* informationNote;
 informationNote = new ( ELeave ) CAknInformationNote;
 informationNote->ExecuteLD( *textResource );

 // Pop HBuf from CleanUpStack and Destroy it.
 CleanupStack::PopAndDestroy( textResource );
 return EKeyWasConsumed;
 }
 }

 // Return the default functionality
 return CCoeControl::OfferKeyEventL( aKeyEvent, aType );
 }

[1] OfferKeyEventL()

virtual TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCode aType);

这 个函数专门用于处理键盘事件,如果对程序的交互和运行需要通过键盘控制,那么视图类就应该去实现这个方法。如果类实现这个方法,特别需要注意的是,若对象 没有对键盘事件作出响应那么应该返回EKeyWasNotConsumed ,反之,若对象对该键盘事件做出了响应那么就要返回 EKeyWasConsumed。当键盘事件发生时,控制框架调用每一个在控件栈中对象的OfferKeyEventL()函数,直到他们中其中的一个可 以处理这个键盘事件并返回EKeyWasConsumed。

参数:
const TKeyEvent& aKeyEvent : 键盘事件。TKeyEvent 类描述了键盘事件的细节,他包括四个属性,分别是iCode, iModifiers, iRepeats,  iScanCode 。当处理一个TKeyEvent的时候,TStdScanCode型的iScanCode通常被TKeyCode型的iCode取 代。

TEventCode aType :键盘事件类型,包括:EEventKey, EEventKeyUp or EEventKeyDown


返回值指明对象是否处理了这个键盘事件。 

任 意一个键盘的按键事件都将导致三个独立的事件:EEventKeyDown, EEventKey和EEventKeyUp,事实上他们触发的顺序也是这 个样子的。为可以获得可以被OfferKeyEventL()函数处理的键盘事件,应用程序必须调用CCoeAppUi::AddToStackL()方 法,把控件压入到栈中。这只是对控件起作用,而不是组成控件的控件组件。复合控件如果有需要的话也可以把键盘事件传递给他们的组件控件,但是组件控件本身 并不可以在控制栈上。

如果一个类覆盖了 CCoeControl::OfferKeyEventL() 方法那么他同时也要覆盖 InputCapabilities() 虚函数,返回一个TCoeInputCapabilities 对象,这个对象的属性符合 OfferKeyEventL()函数的行为。通常没有必要在内部调用InputCapabilities() 方法,而这个方法也一般被UI控制框架调 用。