当前位置:首页 >> IT/计算机 >>

Symbian学习笔记


Symbian 学习笔记

Symbian学习笔记(1):Debug On Device ...............................................................................2 Symbian学习笔记(2):基本概念............................................................................................6 Symbian学习笔记(3):应用程序框架 ..................................................................................11 Symbian学习笔记(4):在GUI应用中使用图像 ...................................................................14 Symbian学习笔记(5):加载JPEG图像的方法.....................................................................17 Symbian学习笔记(6):关于多线程与活动对象 ..................................................................18 Symbian学习笔记(7):定时器..............................................................................................20 Symbian学习笔记(8):再尝试着做一个数独游戏 ..............................................................22 Symbian学习笔记(9):数组..................................................................................................25 Symbian学习笔记(10):使用ListBox ...................................................................................30 Symbian学习笔记(11):初识SettingItemList .......................................................................33 Symbian学习笔记(12):在StatusPane中加上TabGroup ......................................................36 Symbian学习笔记(13):让程序随系统启动的尝试 ............................................................38 Symbian学习笔记(14):使用Browser Control API ..............................................................39 Symbian学习笔记(15) - 解析XML文件(上) ........................................................................43 Symbian学习笔记(16) - 解析XML文件(下) ........................................................................45 Symbian学习笔记(17) - 初探WebServices API的使用(上) .................................................49 Symbian学习笔记(18) - 初探Web Services API 的使用(中) ..............................................54 Symbian学习笔记(19) - 初探Web Services API 的使用(下) ..............................................58 Symbian学习笔记(20) - 用gSOAP更简单地实现Web Services Client ...............................59 Symbian学习笔记(21) - 原来还有这个工具wsdl2cpp,访问webservice也很简单...........62 Symbian学习笔记(22) - 关于皮肤的小结............................................................................64

1

Symbian 学习笔记

Symbian 学习笔记(1):Debug On Device
在 BREW 的开发环境中,没有一个联机调试工具,一直是我觉得相当不方便的事情.在 S60 平台上做开 发,至少这一点是能满足我们的需要. 第一种方法:Carbide C++ v1.2 的 Debug On Device Carbide C++1.2 的 pro.以上版本都支持设备调试,实在是一件大快人心的事. 要实现这个功能,也是相当的方便,当然前提是你用的是 Carbide C++ v1.2 的 Pro.以上版本. 按正常模式写程序,在模拟器上都弄的差不多了,想放到设备中调试的话,只需要做下面几步: 1,先在设备中安装一个软件,安装包在 carbide 的安装目录下,我机器上的位置是 C:\Nokia\Carbide.c++ v1.2\plugins\com.nokia.carbide.trk.support_1.2.0.29\trk\s60, 下面有 两个 sis 文件,因为我要在 N73 上调试,所以我选择其中的 s60_3_0_app_trk_2_7.sisx 这个,另一 个 3_1 是用于 3rd.FP1 手机的.安装后在手机里就有一个 TRK 应用了. 2,安装成功后,在手机上启动这个服务 TRK,因为我是用的 USB 线,所以点选项,改成 USB,端口是 1 (这里比较奇怪,明明我的端口应该是 COM6).启动后窗口显示:Status:Connected. 3,在 Carbide C++中,配置一下编译输出目标为 Phone Debug(GCCE),这个很重要,只有 DEBUG 才行.编译出 SISX 文件. 4,打开 DEBUG 窗口(即 debug...),在配置中的第一项"Symbian OS App Trk"下建立一个新项.几 个窗口的输入如下图所示:

2

Symbian 学习笔记

3

Symbian 学习笔记

5,点 Apply,然后 Debug,就可以启动调试.余下的事情与在模拟器中调试就一样了,没啥好说了. 第二种方法:利用 S60 SDK 自带的 Ecmt 工具进行 DEBUG 如果我们用的是 Carbide C++ 的 Express 版(免费版本)的话,我们没办法做设备联机调试,但是可

4

Symbian 学习笔记 以用 SDK 中的工具在程序中将调试信息打印到控制台上.在 BREW 平台上主要就是依靠这种方式. Symbian 提供了一个 REmct 可以用于远程调试,并且它还提供了两个配套工具:手机端的 ecmtagent_cpp.sis 和 PC 端的 Device Connection. 这种方式比前一种要稍复杂一点了,因为涉及到代码的修改. 1,首先同样是在手机上安装一个软件 ecmtagent_cpp.sis(不过 3rd.的 SDK 下这个东西不能用,说签 名过期之类的错误,只能安装 3rd. FP1 的 SDK 下的那个同名 sis,唉,也不知道 Nokia 是咋回事). 2,同样在调试前也是要启动手机上的 ecmtagent 代理,设置成 USB 连接(不需要选择端口了).然后 看到窗口上显示 listening. 3,在 PC 上启动那个 Device Connection,选择 COM6 去 connect 它,状态成为 connected.这时发 现手机上也显示 connected.表明连接成功了.在 Device Connection 中打开那个 Diagnostics 工具, 所有的调试信息的就是输出到它的窗口里. 4,最麻烦的是需要修改代码. A) 修改 mmp,加上这个 lib.即:LIBRARY B) 在要输出调试信息的那个类的 h 文件中加上: EcmtClient.lib

#ifdef _DEBUG #include <EcmtClient.h> #endif ... ... class CDemoUIAppView : public CCoeControl,MBeating { ... ... private: #ifdef _DEBUG REcmt iEcmt; #endif C) 然后在 cpp 中加上一个初始化与销毁. void CDemoUIAppView::ConstructL( const TRect& aRect ) { CreateWindowL(); ... ... #ifdef _DEBUG iEcmt.Connect(); #endif

5

Symbian 学习笔记

} CDemoUIAppView::~CDemoUIAppView() { ... ... #ifdef _DEBUG iEcmt.Close(); #endif } D) 然后就是在需要输出的地方这样一下: void CDemoUIAppView::Beat() { this->total++; if(this->total>100) { this->total=0; iHeart->Cancel(); } TBuf<16> buf; buf.Format(KMsgFormat,this->total); iLabel->SetTextL(buf); DrawNow(); #ifdef _DEBUG iEcmt.WriteFormat(KFormattedText, this->total); #endif } 5,编译程序,同样是 GCCE 下的 Phone Debug,自己手工安装到手机里,执行它就可以了.要注意的 一点是,因为此时前面的那个 ecmtagent 正在运行,需要将它切到后台去(不是关闭啊!!!).在 Diagnostics 的窗口中就会看到输出的调试信息了. 最后要补充一点的是,我在尝试这两种调试方法的时候,经常遇到打开端口失败或者连接失败的情况,一 般重启一下手机就正常了,不知道是 BUG 呢还是因为我同时弄了两个代理在手机里照成的冲突,如果你 在使用过程中发现不顺了,不妨也重启一下手机吧.

Symbian 学习笔记(2):基本概念

6

Symbian 学习笔记 第一次打开 SymbianC++的程序源代码,第一感觉就是"这是 C/C++吗?咋这么眼生啊". 看代码看书,才算是稍稍明白了一点,其实无非就是 typedef 罢了,symbian 可比 brew 定义了更多的 东西. 这是以后干活的基础,所以做个笔记,省得忘了. 一,基本数据类型 这个比较简单,都在 e32def.h 中写着呢.也就是以下这几个类型要注意一下,以后照着写罢了,含义也很 明了,不用多说. typedef void typedef signed char typedef unsigned char typedef short int typedef long int typedef signed int typedef unsigned int typedef float typedef double typedef double typedef unsigned char typedef unsigned short int typedef int typedef TUint32 二,描述符 这个东西比较有趣, 其实说白了, 也就是两个我们以前常用的玩意儿: String 和 malloc. 不过在 symbian 中把描述符分为三类:缓冲描述符,指针描述符和堆描述符. A.缓冲描述符:TBuf,TBufC 类似于 char[], 也就是说它是一个字符串的表示方法, 比如: TBuf<20> str;与我们以前写的 char str[20]; 意思基本一样. 不过,描述符可以包含一些方法,就象我们用 String 主要就是冲着它的方便的字串处理方法去的. B.指针描述符:TPtr,TPtrC 类似于 char *,也就是说这是一个字符(字节)指针的另类表示罢了. TBool; TLinAddr; //Defines a linear (virtual) address type. typedef unsigned short int TInt32; TUint32; TUint; TReal32; TReal64; TReal; TText8; TText16; TInt; typedef unsigned long int TAny; TInt8; TUint8; TInt16; TUint16;

7

Symbian 学习笔记

C.堆描述符:HBufC 类似于我们用 malloc 开辟的一块空间,比如:HBufC * buf = HBufC::NewL(128);与 byte * buf = (byte*)malloc(128);的意思也是基本一样的. 还有一种抽象的描述符 TDes 和 TDesC,是其它描述符的基类. 所有描述符名称后面的 C 表示它是一个不可修改的描述符.换句话说就是,所有不带 C 的描述符是在带 C 描述符的基础上增加了一些进行修改操作的函数. 要记住的是描述符带给我们的便利. 比如这几个函数: Length() Compare() 得到字串的真实长度(元素个数),而 Size()则是得到它所占的字节数. 是用来获取子字符串的函数. 则可以查找子串或字符. 则可以对描述符的内容进行修改 比较函数.

Left()/Right()/Mid()

Locate()/LocateReverse()/Find()/Match() 操作. Num() Format() 可以将数值转成字符串.

Copy()/Delte()/Insert()/Replace()/Trim()/Append()/Zero()

类似于 sprintf,比较常用,格式化输出.不过还有同系的其它函数也许更方便,如

AppendFormat()/AppendNum()等等. 对于堆描述符,需要注意的地方有三点: 一是 Des(),因为 HBufC 带 C 是不可修改的描述符,所以如果我们要修改它,则需要用 buf.Des()得到 一个指向它的指针描述符.如下句: _LIT(KHello,"hello china"); HBufC * buf = HBufC::NewL(64); *buf=KHello; TPtr p = buf->Des(); p[0]='H'; 二是在 TDesC 中有一个 AllocLC()可以分配内存得到 HBufC 描述符,与 HBufC::NewL()是一样的.而 且 HBufC 中也有 ReAllocL 可以重新分配内存,就象 realloc 一样. 三是区分一下两句话的含义: TPtr p=buf->Des(); TPtr p(buf->Des()); 第一句只是根据 buf 当前的真实长度得到一个指针(p 的最大长度与当前的实际长度一样,就是 buf 此时 的真实长度 11),而第二句则完全用 buf 的信息来构造了 p,所以它的最大长度应该是 64,虽然当前的真

8

Symbian 学习笔记 实长度也是 11. 还有一个与描述符相关的宏很常用,需要注意一下:_LIT(常量名称,字串值).比如: _LIT(KSayHelloSTR,"Hello world."); 而那个_L 宏不提倡用了,因为效率太低的原因. 这里的 KSayHelloSTR 是另一种描述符 TLitC.而 TLitC 提供两个运算符要注意: &操作符能得到它的 const TDesC*,而()操作符则得到它的 const TDesC&. KSayHelloSTR().Length(); //得到这个字串的长度 TBuf<256> str; str.Format(KFormatSTR,&KSayHelloSTR); //得到这个字串的引用地址 具体关于描述符的信息可以参考 H 文件:e32des16.h 三,错误处理 有三个概念,一是 Leave.最常见的地方是对 new 操作符的重载 new(ELeave),表示此时的构造会产生 内存不足的现象. 有了 new(ELeave),我们就可以放心地在 new 一个对象之后直接使用它而不需要去判断是否构造成功, 因为如果不成功会抛出错误并返回上层. 第二个概念配合 Leave,有一个宏 TRAPD(error,Func)类似于 try...catch.... 也就是说如果 Func 函数中发生了 Leave,则 error 能得到错误码.一般在程序中可以用 User::Leave() 来抛出错误,类似于 throw new Exception 的操作. 第三个概念也是最常用的,就是清理栈 CleanupStack 的使用. 取代 TRAPD 宏的使用,我们可以在可能发生 Leave 之前将指针 push 到 cleanupstack 中,在正确完成 之后再将它 pop 出来,如果万一不成功,系统会帮我们将 cleanupstack 中的东西销掉.这样就方便了很 多. 一般的代码类似于: CMyCls * mc=new (ELeave) CMyCls; CleanupStack::PushL(mc); me->doSth1L(); me->doSth2L(); CleanupStack::PopAndDestroy(); 一般我们是将局部变量用 cleanupstack 来保护一下,但是对于类的成员变量则不能这样做(否则会二次 销毁,产生严重错误). 此外,在 pop 时超出范围了,压几个就弹几个,如果不小心把其它的内容给弹出来,也可能会引起严重错 误的.所以,Pop 有一个重载 Pop(3,pA)这里表示弹出三个对象并且比较一下最后出栈的是不是 pA.

9

Symbian 学习笔记

四,两阶段构造 一般 Symbian 的类不会提供 public 的构造函数 (不建议这么做) 因为它提倡所谓的"两阶段构造"方法. , 定义为: A)构造函数是 protected 或者 privted 的,并且不能包含引起 Leave 的操作. B)实现两个静态函数 NewL 和 NewLC,来代替构造函数提供给用户使用. C)实现一个 ContructL 函数实现第二阶段的构造,其实大多数初始化的工作可以放在这里进行. 两阶段构造的代码相当地格式化了,比如两个静态函数 NewL 和 NewLC 的代码一般是这样的: CMyClass * CMyClass::NewL() { CMyClass * self=NewLC(); CleanupStack::Pop(); return self; } CMyClass * CMyClass:NewLC() { CMyClass * self = new(ELeave) CMyClass; CleanupStack::PushL(self); self->ContructL(); return self; } 而在 ContructL 中一般可以做一些真正的构造函数里的操作,例如分配内存,创建窗体之类的活儿. 五,命名规则 这也是一个需要注意的地方,按教材上所言列举如下: 类的名称前缀有 T,C,M 和 R 四个,分述如下: T 表示基本类,它位于栈里,就当作是一个结构吧. C 表示常规的类,继承于 CBase 的,这是 C++标准的类的概念,所以有构造要析构. M 表示是一个接口,很好理解,它肯定含有纯虚函数. R 表示是一个系统资源,比如文件,网络等等,所以它肯定有 Open 有 Close. 还有,K 开头表示常量,E 开头表示枚举也要记得. 形参用 a 开头,类成员变量用 i 开头,这此规则我们在自动生成的代码中也能看到. 还有函数的命名上也有讲究, 不过不是开头而是结尾: L 表示可能会有 Leave, 表示不但可能有 Leave 象 LC 而且它会被自动放在 CleanupStack 中. 还有两个二阶段构造又有三个函数名称固定了:NewL,NewLC 和 ContructL.

10

Symbian 学习笔记

Symbian 学习笔记(3):应用程序框架

开发 symbian 的 GUI 应用是有模板的,用 Carbide C++的工程向导可以自动生成程序的基本框架.不 过可能与安装的 SDK 版本有关, 我现在只能生成一个 AppUi 一个 Container 的常规模板 (以前用 2nd FP3 时还可以生成多个 view 多个 container 的视图模板). 不管是哪一种模板,它们共同的部分是入口函数,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() Application() StatusPane() Cba() 可以获取 Document 对象指针. 可以获取 Application 对象指针. 可以获取状态栏的指针.

可以获取控制栏的指针.

5)有一个以工程名+Container 命名的类,派生于 CCoeControl,它负责内容的展示,也是我们关注的 11

Symbian 学习笔记 重点. 如果需要在界面上增加控件类类的东西,都是在这个类中实现,总的来说,它负责所有与界面展示相关的 东西. 它有一个 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. CiMusicViewMusic* view1 = new (ELeave) CiMusicViewMusic; CleanupStack::PushL( view1 ); view1->ConstructL(); AddViewL( view1 ); CleanupStack::Pop(); // transfer ownership to CAknViewAppUi // view1

CiMusicViewFavt* view2 = new (ELeave) CiMusicViewFavt; CleanupStack::PushL( view2 ); view2->ConstructL(); AddViewL( view2 ); CleanupStack::Pop(); // transfer ownership to CAknViewAppUi // view2

CiMusicViewWeb* view3 = new (ELeave) CiMusicViewWeb; CleanupStack::PushL( view3 ); view3->ConstructL(); AddViewL( view3 ); // transfer ownership to CAknViewAppUi

12

Symbian 学习笔记 CleanupStack::Pop(); // view3

this->ActivateLocalViewL(KViewMusicId); 5)有多个以工程名+View 命名的类,派生于 CAknView 类.它负责分担 AppUi 部分事件的处理,所以, 它也有 HandleCommandL 函数. 此外,它的 DoActivateL 和 DoDeactivate 两个函数在当前视图激活或失活时被调用,需要重载一下. 在激活时,需要创建视图对应的 Container 类,并且调用 Container 的 SetMopParent 为自己,还要在 上层的 AppUi 中将这个 Container 加入栈中.一般代码如下: iContainer = new (ELeave) CiMusicContainerFavt; iContainer->SetMopParent(this); iContainer->ConstructL( AppUi()->ApplicationRect() ); iContainer->listType=type; AppUi()->AddToStackL( *this, iContainer ); 注意,它的 AppUi()可以得到它的上层的 AppUi 对象指针. 失活时则正好相反,需要 if ( iContainer ) { AppUi()->RemoveFromViewStack( *this, iContainer ); } delete iContainer; iContainer = NULL; 6)有多个以工程名+Container 命名的类,派生于 CCoeControl 并实现接口 MCoeControlObserver, 所以它的行为与常规模式中的 Container 类似. 这种模式可以有效地组织应用程序,根据应用的多个功能界面切割成数个模块(视图).由多个 AppView 来分担 AppUi 中的事件处理,负责自己这个视图下的具体行为与显示. 各个视图之间的切换也很简单: STATIC_CAST(CiMusicAppUi*,iCoeEnv->AppUi())->ActivateLocalViewL(KViewMusicId);

[补充] 三 比较两种模式 有些教材还提出过对话框模式,但是我觉得那似乎不太实用,也没注意过它的结构组成. 对比上述两种架构模板,很显然,第二种基于视图的模板应该更有实用性一点,除非程序实在简单.

13

Symbian 学习笔记 不过,这里的"视图"很容易让人产生误解.一般我们说 MVC 的时候,模型-视图-控制器,但是这里的 AppView 其实对应的控制器,而 Container 对应的则是视图. 所以在新的 SDK 里,第一种模板中由向导生成的工程中源于 CCoeControl 的类名改成了 AppView,对 应继承于 CAknAppUi 控制器的类名叫 AppUi. 因为我没有看到多视图的工程生成的代码, 如果这样的话, 估计也应该对应的改一下吧. [补充@2008-03-20] 在多视图构架中要注意几个地方: 1,在 AppUi 的析构函数中千万不要去删除 AppView 的实例,否则会有错误. 2,在 AppView 的析构函数中调用一下 DoDeactivate(),因为只有在这个函数才会去清掉 Container.

Symbian 学习笔记(4):在 GUI 应用中使用图像

今天学习一下如何修改 GUI 程序中的图标与如何加载图像. 与 2nd.不同的是,3rd.支持 svg 格式的可缩放图标(最大好处在于一个图标搞定,以前用 bmp 时为了应 用程序的菜单图标得要做四个小图标).而且,现在有了一种新的图像打包格式 mif(这名字咋跟 BREW 中那个 MIF 一样啊).虽然还支持以前的 MBM 方式但是不建议使用了. 要在应用程序中增加图像图标资源,过程如下: 第一步,先将要加入的图像放在工程的/gfx 目录下,如: qgn_menu_DemoUI.svg splash2.bmp 前面一个就是应用程序图标(可以修改它). 第二步,修改/group 目录下的 Icons_scalable_dc.mk 文件,不知道为什么,工程向导生成的这个文件 相当不完整,郁闷. TARGETDIR=$(ZDIR)\resource\apps ICONTARGETFILENAME=$(TARGETDIR)\DemoUI_0xE8656D58.mif HEADERDIR=C:\Symbian\workspace\DemoUI\inc HEADERFILENAME=$(HEADERDIR)\DemoUI.mbg 上面的 targetdir 是原有的,下面的 headerdir 是我加上的,因为我需要一个 mbg 文件(以前旧方式也 有这个头文件).

14

Symbian 学习笔记

RESOURCE : mifconv $(ICONTARGETFILENAME) /h$(HEADERFILENAME) \ /c32,8 $(ICONDIR)\qgn_menu_DemoUI.svg \ /c24 $(ICONDIR)\splash2.bmp

RELEASABLES : @echo $(HEADERFILENAME)&& \ @echo $(ICONTARGETFILENAME)

主要是那个 mifconv 的参数,加上/h 生成 mbg 头文件,注意这里的/c32,8 表示我们只提供一个 svg 文 件但是将它的 8bit 图像作为它的 mask,比 原来的方式简单多了,当然你也可以新做一个图像作为 mask. [补充@2008-03-14] 昨天这个例子只在模拟器上成功了,真机失败,无法加载 bmp 图像. 后来出编译时的控制台输出发现,它只把 svg 图片放在 mif 文件中,而 bmp 图片仍会放入一个同名的 mbm 文件中.所以我们还得修改 pkg 文件,让 mbm 也打包进 sisx 中: "$(EPOCROOT)Epoc32\data\z\resource\apps\DemoUI_0xE8656D58.mif" -"!:\resource\apps\DemoUI_0xE8656D58.mif" "$(EPOCROOT)Epoc32\data\z\resource\apps\DemoUI_0xE8656D58.mbm" -"!:\resource\apps\DemoUI_0xE8656D58.mbm" 而代码中则无所谓了, 因为 AknIconUtils::CreateIconL()会自动判断是去 mif 还是 mbm 中读取图像资 源. 第三步,先把上面的改完后,编译一下,就能得到 mif 文件和 mbg 文件了. 下面在 container 中显示这个图像 splash2.bmp,先修改.h 文件,增加两个 CFbsBitmap. private: CFbsBitmap* CFbsBitmap* iBkImage; iBkImageMask;

修改 cpp 文件,如此加载和显示: _LIT(KMbmFileName,"\resource\apps\DemoUI_0xE8656D58.mif"); const TInt KMyIconMaxWidth(100); const TInt KMyIconMaxHeight(100);

15

Symbian 学习笔记 ... void CDemoUIAppView::ConstructL( const TRect& aRect ) { ... TFileName fullname(KMbmFileName); CompleteWithAppPath(fullname); iBkImage=AknIconUtils::CreateIconL(fullname,EMbmDemouiSplash); ... } ... void CDemoUIAppView::Draw( const TRect& aRect ) const { CWindowGc& gc = SystemGc(); TRect drawRect( Rect()); gc.Clear( drawRect ); gc.BitBlt(TPoint(0,0),iBkImage); ... } 其实这里与旧方式差不多了,但是原来的那个 iBkImage->Load()方法已经不管用了,只能用 AknIconUtils 提供的这个方法 CreateIconL. 另外,如果是加载 SVG 呢,其实也一样的,如果同时加载 mask 呢?也一样.例如: AknIconUtils::CreateIconL(iBkImage, iBkImageMask, fullname, EMbmDemouiQgn_app_de moui, EMbmDemouiQgn_menu_demoui_mask); AknIconUtils::SetSize(iBkImage, TSize(KMyIconMaxWidth, KMyIconMaxHeight)); AknIconUtils::SetSize(iBkImageMask, TSize(KMyIconMaxWidth, KMyIconMaxHeight)); 显示时就这样: TPoint point = TPoint(aRect.Center().iX - (KMyIconMaxWidth/2),aRect.Center().iY - (KMyIc onMaxHeight/2)); gc.BitBltMasked(point, iBkImage, aRect, iBkImageMask, EFalse);

16

Symbian 学习笔记 今天找了找 SVG 的编辑器,觉得那个 RealDraw 还行. [补充@2008-03-20] 关于 SVG 格式的显示问题,需要补充两点: 1,它必须要设置尺寸才可以正确显示,虽然我们在制作 SVG 时给了画布尺寸在 SVG 文件中也有尺寸信 息,但是如果代码中不调用 AknIconUtils::SetSize,则无法显示,不报错就是不显示,很 BT 的问题. 2,关于 Mask 的问题,因为 SVG 不是透明的(在制作 SVG 时画布颜色可以选白色),可以用 MASK 来 制作透明效果,也就是白色部分作为透明色的话则 BitBltMasked 函数最后一个参数为 EFalse,否则如以 黑色为透明它应该是 ETrue. 如果需要 Mask,别忘了在 mk 文件中这样:/c32,8 表示原图是 32 位而用它的 8 位图作为 mask 图像.

Symbian学习笔记(5):加载JPEG图像的方法
上次学习的是从 MIF 和 MBM 文件中加载图像,一般在程序中出现的图片都可以用这个方法,但有一点点 遗憾,它只能使用 svg 或 bmp 格式,BREW 或 J2ME 中的资源却多为 png 格式的.

如果想在 symbian 中使用 jpg 或者 png 图片,则需要用到 symbian 提供的那个 Multimedia ICL 库里的类了.其中比较重的是 CImageDecoder 类,提供了对图像进行 解码的功能. 来看看如何使用它的吧.先定义两个字串: _LIT(KJPEGFile,"C:\\Data\\me.jpg"); _LIT(KJPEGType,"image/jpeg"); 使用的代码比较简单,如下所示:
iDec = CImageDecoder::FileNewL(iRFs,KJPEGFile); iParent->iBkImageMask=new (ELeave) CFbsBitmap(); iParent->iBkImageMask->Create( iDec-> FrameInfo().iOverallSizeInPixels,iDec-> Fram eInfo().iFrameDisplayMode ); iDec->Convert( &iStatus, *(iParent->iBkImageMask) );

其中的 iDec 是 CImageDecoder 的实例,而 iRFs 是 RFs 的实例.而 iParent->iBkImageMask 则是一个 CFbsBitmap 对象实例. 也就是说,我们先利用 CImageDecorder::FileNewL 来打开一个图像文件(它会自 已判断图像类型,我们也可以提供给它我们指定的图像类型).然后这个图像的信息就保存 在 iDec->FrameInfo()中了,利用这个信息可以创建一个 CFbsBitmap 实例(因为 symbian 中所有显示的图像都是这种类型).最后,调用 CImageDecorder 的异步函数 Convert 就可以将图像(jpeg 或者 png 格式的)转换成 CFbsBitmap 位图了.唯一的麻
17

Symbian 学习笔记

烦是 Convert 是一个异步函数,第一个参数是 TRequestStatus 类型(可以是一个活动 对象的 iStatus 成员). 除了 CImageDecorder::FileNewL 以外还有一个 CImageDecorder:;DataNewL 也可以用来从一个缓冲区的字节内容构造出图像数据,只是它第二个参数是 TDesC8&类型 的 aSourceData. [补充] 别忘了包括头文件 ImageConversion.h,还有引入库 imageconversion.lib.

Symbian 学习笔记(6):关于多线程与活动对象

symbian 支持多线程(它有一个 RThread

类)但却又不提倡大家使用多线程,理由一般 是说应用更安全?代码更简单?不过我觉得最有说服力的是省电. 既然如此,那我们就不说"多线程",说说"多任务"吧,活动对象(Active Object)提供了非抢占式的多任务协同处理机制,它不是多线程的,而是运行在 一个单线程中. AO 机制包括两个类 CActiveScheduler 和 CActive: 调度器 CActiveScheduler:顾名思义它是协调多个活动对象的调度者.既然是 非抢占式的,那它的调度原则是什么呢?教材上说法是: A) 根据活动对象的优先级顺序对已注册的所有活动对象逐个检查. B) 判断它是否是活动的(IsActive),且它的 iStatus 是否不为 KRequestPending. C) 满足则执行它的 RunL 方法. 活动对象 CActive:这可以理解为一个"线程",就象 Java 中的 Runable.它受 调度器的指挥,它的关键在于类型为 TRequestStatus 的 iStatus 成员变量. 一般情况下在 CActive 中会有一个异步操作(方法形参表中包括一个 TReuqestStatus 类型参数),如果没有也无所谓(根据上面的调度原则,只要 保证它不为 KRequestPending 即可). 因为 GUI 应用自带了 CActiveScheduler,所以我们要做的事情就是实现一个派 生于 CActive 的子类,重载方法 RunL(),DoCancel()和 RunError().当然还应 该有一个类似于 Start 的方法来启动它. Start 负责启动,一般是在这里去执行一个异步操作(比如加载 JPEG 的图像, 如上篇所言;或者打开一个定时器,如书上例子).如果没有异步操作的话,也
18

Symbian 学习笔记

可以在这里啥事都不做. 但是无论如何, 它的最后一行肯定应该是 SetActive(), 让自己成为活动状态. RunL 负责具体的工作,如果是刚才 Start 了一个异步操作,此时应该处理该操 作结束后的事务(如上篇中加载了图像以后就可以显示到屏幕上了).如果刚才 没有 Start 一个异步操作,那现在也得做点事情了. 有一种情况,我们把一个大任务分解成多个小任务,放在 RunL 中来做,那就得 再加一个状态 TInt iState 来记录此次回调应该做哪一步小任务了(每完成一个 小任务就改一个 iState 的值,并且还得再 SetActive,让这个活动对象继续处 于活动状态,直到所有任务完成). DoCancel 提供了用户可以中止活动任务的手段,而 RunError 则提供了错误处理 的机会. 另外,在这个 CActive 的子类中,应该有一个地方(一般是在 ConstructL 中) 将自己放入调度器的队列中,即调用 CActiveScheduler::Add(this). 帖一段代码,就是上篇加载 JPEG 图像的代码应该放在一个 AO 中来实现,利用向 导生成一个 CActive 的子类: class CImageLoader : public CActive { public: ~CImageLoader(); // Two-phased constructor. static CImageLoader* NewL(CDemoUIAppView *p); static CImageLoader* NewLC(CDemoUIAppView *p); public: // New functions void StartL( ); private: CImageLoader(); void ConstructL(); private: // From CActive void RunL(); void DoCancel(); TInt RunError( TInt aError ); private: RFs iRFs; CDemoUIAppView *iParent;

19

Symbian 学习笔记

CImageDecoder };

*iDec;

cpp 中的实现更简单了,在 StartL 中负责刚才的加载 jpeg 图像(主要是它有一 个异步调用),在 RunL 中负责重绘 CDemoUIAppView 的界面即可. void CImageLoader::StartL( ) { Cancel(); // Cancel any request, jus t to be sure if(iDec) iDec->Cancel(); delete iDec; iDec=NULL; iDec = CImageDecoder::FileNewL(iRFs,KJPEGFile); iParent->iBkImageMask=new (ELeave) CFbsBitmap(); iParent->iBkImageMask->Create( iDec-> FrameInfo().iOverallSize InPixels,iDec-> FrameInfo().iFrameDisplayMode ); iDec->Convert( &iStatus, *(iParent->iBkImageMask) ); SetActive(); is active } void CImageLoader::RunL() { if(iParent) iParent->DrawNow(); } // Tell scheduler a request

Symbian学习笔记(7):定时器
教材里说到活动对象时, 总拿 CTimer 来做例子, CActive 派生一个 CMyActive 然后它包括一个 CTimer 从 iTimer,再利用它的异步函数 iTimer.After 来演示活动对象的效果. 但是 CTimer 本身就已经是源于 CActive 了,所以我今天来讨论的是直接使用定时器,必竟在手机上定时 器是一个比较常用的功能(在 BREW 开发中因为没有多线程,几乎所有的应用都会用上那个 ISHELL_SetTimer). CTimer 有两个子类 CPeriodic 和 CHeartbeat,都可以处理周期性的定时器回调,其中心跳当然是更有 规律一些了,它的使用也稍稍麻烦一点.

20

Symbian 学习笔记

先看看心跳的使用吧.修改一下我们的一个视图: class CDemoUIAppView : public CCoeControl,MBeating ...{ //省略部分代码 public: void Beat(); void Synchronize(); void StartTimer(); private: CEikLabel* TInt public: CHeartbeat* } 其中 MBeating 接口定义了两个方法 Beat(每次心跳时调一下它)和 Synchronize(跟系统时钟同步一 下心跳频率). void CDemoUIAppView::ConstructL( const TRect& aRect ) { CreateWindowL(); //创建一个标准优先级的心率定时器 total=0; iHeart=CHeartbeat::NewL(CActive::EPriorityStandard); iLabel=new(ELeave)CEikLabel; iLabel->SetContainerWindowL(*this); SetRect( aRect ); ActivateL(); } //在每次心跳的时候将 total 加 1,重绘 iLabel void CDemoUIAppView::Beat() { this->total++; if(this->total>100) { this->total=0; iHeart->Cancel(); } TBuf<16> buf; iHeart; iLabel; total;

21

Symbian 学习笔记 buf.Format(KMsgFormat,this->total); iLabel->SetTextL(buf); DrawNow(); } //暂时不用同步 void CDemoUIAppView::Synchronize() { return; } //启动 void CDemoUIAppView::StartTimer() { this->iHeart->Start(ETwelveOClock,this); } 注意到 iHeart->Start 的方法第一个参数 ETwelveOClock 在枚举 TTimerLockSpec 中定义,按 1/12 到 1 秒这样划分定时间隔. 如果我们想用 CPeriodic 来做定时器的话,不需要实现什么接口了,只需要在 Start 的时候提供一个回调 函数就可以了.

Symbian 学习笔记(8):再尝试着做一个数独游戏

仍是做一个同样界面同样功能的数独游戏, 但是所花费的时间远远超出我用Android或者J2ME来做同样的 事情,当然运行效率也很明显(我将J2ME实现的数独放在N73 上跑过,因为重绘屏幕的代码没有优化, 所以效果不好,但同样的算法在BREW或者Symbian上就跑得相当顺畅). 言归正传,关于数独在Android上的实现,可参考这个: http://blog.csdn.net/sharetop/archive/2008/02/27/2124153.aspx 这次我主要谈谈在做这个 Symbian版本中遇到的几个问题,希望以后自己不要再犯同样的错误了. 1,多视图的应用程序框架的问题 我用的是Carbide C++ IDE环境,不知道为什么不能生成多视图的架子,所以只能在HelloWorld框架的 代码上自己手工改写, 结果就弄出一个Panic折腾了我很久, 问题出在千万不要在析构函数中去删除创建的 CAknView,如下代码(注掉是正确的,否则就在退出应用时会出错): CSuDoScAppUi::~CSuDoScAppUi() { // // // // if(iAppCtrol!=NULL){ this->RemoveView(iAppCtrol->Id()); delete iAppCtrol; iAppCtrol=NULL; 22

Symbian 学习笔记 // // // // } 我们需要负责清除对象的地方是在 CAknView 中负责删掉 CCoeControl,因为在它的 DoActivateL/DoDeactivateL 两个函数中负责创建和删除 CoeControl,但是如果退出时这个 DoDeactivateL 是不会主动调用的,因此在析构时可以调用一下它: CSuDoScAppCtrol::~CSuDoScAppCtrol() { iFs.Close(); iList.Close(); DoDeactivate(); RDebug::Printf("delete Ctrol"); } 2,关于显示字体的问题 在调用 CWindowGc 的 DrawText 方法时,必须要设置一下字体否则不会有东西显示出来,而设置字体 有一段标准代码可以参考(来源于网上): CWindowGc& gc = SystemGc(); CFont *font = NULL; TFontSpec fontSpec = iEikonEnv->LegendFont()->FontSpecInTwips(); fontSpec.iHeight -= fontSpec.iHeight / 6; iCoeEnv->ScreenDevice()->GetNearestFontInTwips( font, fontSpec ); gc.UseFont( font ); // do sth. gc.DiscardFont(); iCoeEnv->ScreenDevice()->ReleaseFont( font ); 3,关于 SVG 图像的显示 我将所有的数字小图片都做成 SVG 格式,在显示 SVG 图像的时候必须设置它的尺寸,否则也无法显示. 另外,因为制作时它的背景色为白色,所以要显示为透明则需要用到它的 MASK 了. for(TInt i=0,j=0;i<9;i++,j+=2){ CFbsBitmap * b=NULL; CFbsBitmap * c=NULL; AknIconUtils::CreateIconL(b,c,iMFileName,EMbmSudoscY1+j,EMbmSudoscY1+j+1) ; AknIconUtils::SetSize(b, TSize(22, 22)); } RDebug::Printf("delete Ui"); RDebug::Printf("delete iAppCtrol");

23

Symbian 学习笔记 AknIconUtils::SetSize(c, TSize(22, 22)); iThumbNormal.AppendL(b); iThumbNormalMask.AppendL(c); } iThumbNormal 和 iThumbNormalMask 是两个 RPointerArray<CFbsBitmap>对象,这里我在 ContructL 时就加载所有的数字图,这种方法可以有效地减轻在重绘时加载照成的闪屏.然后在显示时这 样即可: if( CSuDoScHelper::CheckGrid(iGrid,i%9,i/9,iGrid[i].value) ) gc.BitBltMasked(TPoint(xx,yy),iThumbNormal[iGrid[i].value-1],r,iThumbNormalMask[i Grid[i].value-1], EFalse); else gc.BitBltMasked(TPoint(xx,yy),iThumbError[iGrid[i].value-1],r,iThumbErrorMask[iGrid[ i].value-1], EFalse);

4,关于按键处理 我仍是按 BREW 手机的模式,左软键菜单,右软键切换模式,*#则用来切换题目.在 Symbian 中对键 值的定义比其它平台要复杂一些,让人有点摸不着头脑,比如数字健没有定义在头文件中(不是那个 EStdKeyNkp1 等枚举),只能自己去尝试一下: TKeyResponse CSuDoScAppView::OfferKeyEventL(const TKeyEvent& aKeyEvent,TEventCo de aType) { if(aType==EEventKeyUp) { if( aKeyEvent.iScanCode>=EStdKeyLeftArrow && aKeyEvent.iScanCode<=EStdK eyDownArrow) return TreatNavKey(aKeyEvent,aType); else if( aKeyEvent.iScanCode>=48 && aKeyEvent.iScanCode<=57) return TreatNumKey(aKeyEvent,aType); else if( aKeyEvent.iScanCode==EStdKeyDevice1 || aKeyEvent.iScanCode==ESt dKeyHash || aKeyEvent.iScanCode==EStdKeyNkpAsterisk) return TreatChrKey(aKeyEvent,aType); } return EKeyWasConsumed; } 5,关于消息对话框 在显示"解题成功"的对话框时,记得不要画蛇添足地去消除对话框,如下面代码中注掉的代码是多余的: TBool CSuDoScAppView::ShowSuccessDlg() { if(iState==EGSTATE_WRITE && CSuDoScHelper::CheckSuccess(iGrid)){ TBuf<32> str;

24

Symbian 学习笔记 iCoeEnv->ReadResource(str,R_TEXT_SUCCESS); CAknInformationNote * dlg = new(ELeave) CAknInformationNote(ETrue); //CleanupStack::PushL(dlg); dlg->ExecuteLD(str); //CleanupStack::PopAndDestroy(1); return ETrue; } return EFalse; } 暂时就这些吧.

Symbian 学习笔记(9):数组

数组是一个很基本的东西了,但是在 Symbian 中也做了比较人性化的封装,让我们可以省却不少代码, 当然也让我们这些新手有点摸不着头脑. 三个类比较实用,需要掌握:TFixedArray,RArray 和 RPointerArray. TFixedArray 它是最类似于我们平时使用的数组,从概念上或者从使用上都非常类似. //声明 TFixedArray<TGridCell,81> iGrid; //使用 void CSuDoScAppView::InitGrid() { for(TInt i=0;i<81;i++) { if(iCurQs->data[i]==0) { iGrid[i].state=TGridCell::ECSTATE_EMPTY; } else { iGrid[i].state=TGridCell::ECSTATE_FIXED; } iGrid[i].value=iCurQs->data[i]; for(TInt j=0;j<9;j++) iGrid[i].tries[j]=0; } 25

Symbian 学习笔记 if(iHeart!=NULL){ iHeart->Cancel(); iTime=0; iHeart->Start(ETwelveOClock,this); } } 把它当成普通的数组来用就 OK 了,一般也就是用它重载的运算符[]和 Count(),不需 new 也不需要 del ete. RArray 从名字上看它以 R 开头,至少说明一点,在使用完后我们需要将它 Close 掉.在使用上也比较简单,类似 于 ArrayList 吧.比如下面代码: //声明 RArray<TQuestion> //使用 void CSuDoScAppCtrol::LoadQuestions() { RFile file; TBuf8<4> bufC4; TBuf8<81> bufC81; if(KErrNone==file.Open(iFs,KTiDataFile,EFileRead)) { file.Read(bufC4,4); TUint len=((TUint)bufC4[3]<<24)+((TUint)bufC4[2]<<16)+((TUint)bufC4[1]<<8) +(TUint)bufC4[0]; for(int i=0;i<len;i++){ TQuestion ti; bufC4.FillZ(); file.Read(bufC4,4); ti.code=((TUint)bufC4[3]<<24)+((TUint)bufC4[2]<<16)+((TUint)bufC4[1]<<8) +(TUint)bufC4[0]; bufC4.FillZ(); file.Read(bufC4,4); ti.time=0; bufC81.FillZ(); file.Read(bufC81,81); for(TInt i=0;i<81;i++)ti.data[i]=bufC81[i]; iList.Append(ti); } file.Close(); iList;

26

Symbian 学习笔记 } } 除了在析构函数中需要 iList.Close()以外,事先不需要做什么初始化工作,一个个的 Append 进去就可以 了.它也重载了运算符[],可以简单地访问其中的元素. 另外,RArray 还提供了插入,查找,排序等功能. RPointerArray 名字上说明它与指针有关,的确它与 RArray 的差别在于它不是保存对象的副本而是保存对象的指针.所 以,使用上需要自己负责将元素删除. //声明 RPointerArray<CFbsBitmap> iThumbNormal; //使用 for(TInt i=0,j=0;i<9;i++,j+=2){ CFbsBitmap * b=NULL; CFbsBitmap * c=NULL; AknIconUtils::CreateIconL(b,c,iMFileName,EMbmSudoscY1+j,EMbmSudoscY1+j+ 1); AknIconUtils::SetSize(b, TSize(22, 22)); AknIconUtils::SetSize(c, TSize(22, 22)); iThumbNormal.AppendL(b); iThumbNormalMask.AppendL(c); } //删除 for(TInt i=0;i<9;i++){ delete iThumbNormal[i]; delete iThumbNormalMask[i]; } iThumbNormal.Close(); 此外,虽然 SDK 中我们也看到许多 CDesXXXX 的数组类,但是我觉得上面三个类应该可以应付我们大多 数的场合了. [补充@2008-3-24] 因为上面三个数组是平台缓冲的,所以并不适合在插入较多的场合,如果你的数组经常插入元素,还是建 议使用 CArrayXXXXXSeg 之类的数组类来实现. [补充@2008-3-25] 谢谢 bluepac 网友的讨论,现在我补充说明一下:

27

Symbian 学习笔记

虽然我觉得上述三个数组类可以解决我们大多数的应用场合,但并非它们就是万能的了. Symbian 不提倡我们直接使用 C++数组, 就象不提倡我们直接使用 int,char 一样, 它提供了大量的有益 的替代类.我们只需要记住:既然在 Symbian 平台上开发,就要听话哦.反正我从小都一直是听话的孩 子. 还有两类数组,一类是描述符作为元素的 CDesCXXX or CPtrCXXX.很好理解它们的元素都是描述符罢 了,只是注意它的名称后面有一个 Flat 或者 Seg,表示是平面(连续空间,访问效率高)还是分段(非连 续空间,适于插入较多的场合). 另一类就是 CArrayXXX 之类的动态数组了,它们也相当重要.与描述符数组不同,它们用模板声明元素 类型. CArrayFixFlat<class T> or CArrayFixSeg<class T>,名字中的 Fix 则 要求元素是定长的对象,可 以是 T 类或者 C 类. CArrayPtrFlat<class T> or CArrayPtrSeg<class T>,因为是指针就无所谓元素要求定长了. 而 RArray<class T>其实也有同样的要求是定长对象,其实很好理解,除了用指针以外,数组怎么都要 求是定长对象放进去. 再次感谢 bluepac 对我的帮助.

#bluepac 发表于 2008-03-24 14:37:28 IP: 58.33.231.* RPointerArray 和 RArray 区别不仅在于指针吧 ! 如果你使用 RPointerArray,那么它的元素一定是要从 CBase 继承 的 , 你用的 CFbsBitmap 就是从 CBase 继承 2008-03-24 15:29:36 作者回复 谢谢,你说的对.<br />我的意思是:<br />因为 RArray 是将对象复制一份 保存,所以它的元素可以是 T 类型或者 C 类型.<br />但是 RPointerArray 因为只保存指针,所以它的元素内容肯定是在堆里,所以它的元素肯定是 C 类 型. <br />所以它的根本区别在于 RPointerArray 是保存指针的. <br /><br /><br /> #bluepac 发表于 2008-03-24 16:28:33 IP: 58.33.231.*

官方有两句话: 1.Note that using C++ arrays of CBase-derived types is not recommended, as objects in the array will not be zero-initialised (as there is no operator new[] member). You should use an array class such as RPointerArray instead for arrays of CBase-derived types. 2.RArray ---A simple and efficient array of fixed length

28

Symbian 学习笔记 objects 我觉得 RArray 的 element 是 C-class 时,还是注意 objects 是否 为 fixed length 而 T-class 是这样解释的:value classes, that do not own any external object :)

2008-03-24 16:53:35 作者回复 不好意思,我觉得你理解有偏差.你说的文档中讨论的不是 RArray 与 RPointerArray 的区别,它说的 C++ array of CBase-derived 是说的那些 CArrayXXXX 之类的东西. #bluepac 发表于 2008-03-24 16:51:39 IP: 58.33.231.* 还有 CDesCArray 以及派生的两个类 CDesCArrayFlat, CDesCArraySeg 是用于 Descriptors Arrays. 虽然说也属于 Array,但是呢,它从 CDesCArray 继承的了丰富的方 法 所以呢,CDes.... 适用的 Descriptors Arrays 应用还是比较多滴 PS: 我是专挑 LZ 的刺儿啊,哈哈 2008-03-24 16:57:40 作者回复 谢谢你挑刺.这是学习笔记.我又不是什么高手,只是新手罢了.的确 CArrayXXXX 之类的东西也很重要只是 R 开头的两个从效率上建议使用罢了. #bluepac 发表于 2008-03-24 17:48:34 IP: 58.33.231.* "它说的 C++ array of CBase-derived 是说的那些 CArrayXXXX 之类的东西." 是这样的吗?我不同意 CArrayXXXX 都是从 CBase 继承的,而 C-class in symbian 有一 个共同的特点:Initialisation of the CBase derived object to binary zeroes through a specific CBase::operator new() - this means that members, whose initial value should be zero, do not have to be initialised in the constructor. 所以这个"C++ array of CBase-derived" 应该类似如下的定义 class CMyclass ; CMyclass myclass<6>; 当然 symbian 中不建议使用 c++ array . CArrayXXXX 不等于 C++ array of CBase-derived. CArrayXXXX 和 RArray/RPointerArray 共同构成了一个强大的

29

Symbian 学习笔记 Dynamic Arrays. (相互切磋,共同进步)

2008-03-25 10:32:01 作者回复

Symbian 学习笔记(10):使用 ListBox
symbian 中的 ListBox 比较复杂也是比较常用的,我只能先从最简单的 CAknSingleStyleListBox 入手 来尝试看看.太复杂的东西不是我这样的新手要立刻去明白的. 先声明一个列表组件:CAknSingleStyleListBox* 然后在 Container 的 ConstructL 中去创建它: void CUniNewsAppContainer::ConstructL(const TRect& aRect) { CreateWindowL(); //add your code here ... //construct a listbox iListBox = new(ELeave) CAknSingleStyleListBox iListBox->SetContainerWindowL( *this); iListBox->SetListBoxObserver(this); iListBox->ConstructL(this,EAknListBoxSelectionList|EAknListBoxLoopScrolling); iListBox->CreateScrollBarFrameL(ETrue); iListBox->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOn,CEikScr ollBarFrame::EOn); iListBox->ItemDrawer()->ColumnData()->EnableMarqueeL(ETrue); iListBox->SetRect(aRect); SetRect(aRect); ActivateL(); } 这里有几句话,一是 SetScrollBarVisibilityL 设置使用滚动条,二是 ItemDrawer()->ColumData()->EnableMarqueeL()让选中的文本超长后可以左右滚动. 有一点比较奇怪,我得先设置 ListBox 的 Rect 才能设置整个 Container 的 Rect?否则 ListBox 会不占 整个主面板的位置. 接着在合适的地方需要去给 ListBox 增加内容: void CUniNewsAppContainer::InitListBox(TInt tabId) { iListBox;

30

Symbian 学习笔记 if(iListBox==NULL) return;

CDesCArray* list = static_cast<CDesCArray*>( iListBox->Model()->ItemTextArray() ); TBuf<256> str; list->Reset(); CUniNewsAppView * appView = STATIC_CAST(CUniNewsAppUi*,iCoeEnv->AppUi())-> iAppView; RArray<TNewsContent>* rc = appView->iChannelHandler->GetContents(); for(TInt i=0;i<rc->Count();i++) { if( (*rc)[i].pid==tabId){ str.FillZ(str.MaxLength()); str.Format(KITEMFORMAT,(*rc)[i].title); list->AppendL(str); } } iListBox->HandleItemAdditionL(); iListBox->SetFocus( ETrue ); iListBox->SetCurrentItemIndexAndDraw(appView->iListIndex); iListBox->ActivateL(); iListBox->DrawNow(); } 这里的 HandleItemAdditionL()通知一下 ListBox 模型作了增加操作,同样还有一个 HandleItemRemovalL()则是通知 ListBox 作了一个删除操作. 这里的 KITEMFORMAT 定义是"\t%S".这里的格式似乎挺重要的,一般是:图标 ID\t 题头字串\t 主要 字串\t 图标 ID\t 图标 ID. 我这里因为没有用到图标所以是一个\t%S,这个\t 不可省略.如果用图标,则变成%d\t%S 了,同时还 要增加 iconArray 在创建 iListBox 的时候. CAknIconArray* icons =new(ELeave) CAknIconArray(2); CleanupStack::PushL(icons); icons->ConstructFromResourceL(R_ICON_LISTICONS); iListBox->ItemDrawer()->ColumnData()->SetIconArray(icons); CleanupStack::Pop(); 在 ListBox 中有一个叫 Model()的还有一个叫 View()的,从名字上就可以看出它们的含义了.前面我们 从 Model 中操作列表内容,而我们可以从 View 中获取 ItemDrawer 去操作列表显示

31

Symbian 学习笔记 的一些参数,但是我觉得有一点不爽的是,缺省生成的列表框字体比较大,不是太喜欢,在网上搜了一下, 似乎那个设置字体的方法对我的机器不管用? iListBox->ItemDrawer()->SetFont(ApacPlain12()); 不过有一种方法是可行的,只是比较麻烦,那就是自己去实现 ListBox,以及它的 ItemDrawer.在网上 看到的代码我试了一下,还行.方法如下. 第一步作一个自己的 ItemDrawer: class CCustomListItemDrawer: public CListItemDrawer { public: CCustomListItemDrawer(const CEikTextListBox& aListBox); ~CCustomListItemDrawer(); private: virtual void DrawActualItem(TInt aItemIndex, const TRect& aActualItemRect, TBool aItemIsCurrent, TBool aViewIsEmphasized, TBool aViewIsDimmed, TBool aItemIsSelected) const; public: void SetIconArray(CArrayPtr<CGulIcon>* aIconArray); TSize MaxIconSize() const; private: void DeleteIconArray(); void CalculateMaxIconSize(); private: const CEikTextListBox& iListBox; CArrayPtr<CGulIcon>* TSize }; iIconArray; iMaxIconSize;

实现的代码中最重要的就是那个 DrawActualItem 方法负责具体的绘制工作, 从它的参数表中足够得到绘 制所需的信息,剩下的事情就是用 SystemGc 去绘制. 第二步是作一个自己的 ListBox 控件: class CCustomListBox: public CEikTextListBox { public: // constructors CCustomListBox(); void ConstructL(const CCoeControl* aParent, TInt aFlags = 0);

32

Symbian 学习笔记

public: // from CEikTextListBox virtual TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType); private: // from CEikTextListBox virtual void CreateItemDrawerL(); }; 在 它 的 CreateItemDrawerL() 中 创 建 成 员 iItemDrawer = new (ELeave)

CCustomListItemDrawer(*this).而 OfferKeyEvent 主要的作用是处理上下方向键.

Symbian 学习笔记(11):初识 SettingItemList

在 BREW 中要做一个配置窗口是非常麻烦的事情,而 S60 的配置列表却相当方便.尤其是利用资源文件 来构造配置页,在 RSS 中写的东西有点复杂但是代码却简化了不少. 先看简单的代码,需要自己构造一个 SettingItemList 类派生于 CAknSettingItemList 即可: class CUniNewsSettingItemList: public CAknSettingItemList { public: void ConstructL(); CAknSettingItem* CreateSettingItemL( TInt aIdentifier ); public: TBool TTime }; 其中,iDown 和 iTime 是两个需要配置的内容(这个例子中我需要在配置页让用户选择"是否自动下载" 以确定"自动下载的时间".) 然后在实现代码中这样: void CUniNewsSettingItemList::ConstructL() { CAknSettingItemList::ConstructFromResourceL( R_CFG_ITEM_LIST ); } CAknSettingItem* CUniNewsSettingItemList::CreateSettingItemL(TInt aIdentifier) { CAknSettingItem* settingItem = NULL; iDown; iTime;

33

Symbian 学习笔记 switch (aIdentifier) { case EMySettingItemBinary: settingItem = new (ELeave) CAknBinaryPopupSettingItem( aIdentifier, iDown); break; case EMySettingItemTime: settingItem = new (ELeave) CAknTimeOrDateSettingItem( aIdentifier,CAknTimeOrDateSettingItem::ETime,iTime); break; } return settingItem; } 在 ConstructL 中简单地调用基类的函数从资源中生成配置页,而在 CreateSettingItemL 函数里根据 aIdentifier 去资源文件中读取相应的描述来生成配置项. 创建一个 BOOL 配置项可用 CAknBinaryPopupSettingItem,它在构造函数第二个参数就是 iDown 成 员,这样关联一下就把这个配置项与成员 iDown 关联上了,同理,创建一个 CAknTimeOrDateSettingItem 实例,与 iTime 成员关联上.相关的配置项有很多,在 SDK 中可以找 到它们的说明:API Reference Guide/C++API Reference/UI Framework/Setting Pages API. 代码差不多就这样了,使用与普通的 ListBox 没什么两样,只需要在保存和加载时注意一下即可. 在显示配置页时,记得先 iListBox->LoadSettingsL()一下就让 iListBox 从成员变量中加载数据,而在 保存时也同样 iListBox->StoreSettingsL()让成员变量从配置页中得到配置结果. 现在来看看 RSS 中的描述,相对于代码来说,这稍有点复杂了. RESOURCE AVKON_VIEW r_view_cfg { cba = R_AVKON_SOFTKEYS_DONE_CANCEL; } RESOURCE AVKON_SETTING_ITEM_LIST r_cfg_item_list { flags = EAknSettingItemNumberedStyle; title = qtn_cfg_title; initial_number = 1; items = { AVKON_SETTING_ITEM {

34

Symbian 学习笔记 identifier = EMySettingItemBinary; setting_page_resource = r_binary_setting_page; associated_resource = r_popup_setting_binary_texts; name = qtn_cfg_autodown; }, AVKON_SETTING_ITEM { identifier = EMySettingItemTime; setting_page_resource = r_time_setting_page; name = qtn_cfg_autotime; } }; } RESOURCE AVKON_SETTING_PAGE r_binary_setting_page { number = 1; label = qtn_cfg_autodown; type = EAknCtPopupSettingList; editor_resource_id = r_binary_popup_setting_list; } RESOURCE AVKON_POPUP_SETTING_TEXTS r_popup_setting_binary_texts { flags = 0; setting_texts_resource = r_on_off_texts; popped_up_texts_resource = r_popped_up_on_off_texts; } RESOURCE ARRAY r_on_off_texts { items = { AVKON_ENUMERATED_TEXT { value = 1; text = qtn_cfg_autodown_on; }, AVKON_ENUMERATED_TEXT { value = 0; text = qtn_cfg_autodown_off; } }; } RESOURCE ARRAY r_popped_up_on_off_texts { items = { LBUF { txt = qtn_cfg_autodown_on; }, LBUF { txt = qtn_cfg_autodown_off; } }; } RESOURCE POPUP_SETTING_LIST r_binary_popup_setting_list {

35

Symbian 学习笔记 flags= 0; } RESOURCE AVKON_SETTING_PAGE r_time_setting_page { number = 2; label = qtn_cfg_autotime; type = EEikCtTimeEditor; editor_resource_id = r_settinglist_time_editor; } RESOURCE TIME_EDITOR r_settinglist_time_editor { minTime = TIME { second = 0; minute = 0; hour = 0; }; maxTime = TIME { second = 59; minute = 59; hour = 23; }; }

我也是按网上找到的资料来改写的.虽然看上去有点多,不过看明白了其实还是相当有规律的,应该比在 代码中写大堆的东西要简便一点. 其实也可以完全在代码中动态生成配置页而不依靠资源文件中的描述.但是那样做太复杂了,我以后再去 研究吧.

Symbian 学习笔记(12):在 StatusPane 中加上 TabGroup

在 symbian 中的 tabgroup 的使用比较常见,在多视图的框架中就提供了一些代码. symbian 把整个屏幕分成三大块上面的状态面板(StatusPane),中间的主面板和下面的控制面板 (ControlPane).控制面板中就是那个 CBA 的东西,暂且不管. 状态面板中可以让我们去操作的包括:标题栏(可以修改显示标题,缺省是应用程序名称),上下文栏(就 是大图标的位置),还有一个就是导航栏了(用来显示 TabGroup 或者一些文字信息比如日期,编辑模式 之类的).

36

Symbian 学习笔记

在代码中操作状态栏的方法并不复杂,在 AppUi 中有一个 StatusPane()可以轻松的得到状态栏的引用. iNaviPane = (CAknNavigationControlContainer*)StatusPane()->ControlL(TUid::Uid(EEikStatusPan eUidNavi)); iDecoratedTabGroup = naviPane->ResourceDecorator(); if (iDecoratedTabGroup){ iTabGroup = (CAknTabGroup*) iDecoratedTabGroup->DecoratedControl(); iTabGroup->SetObserver( this ); } 上面的代码是用来从资源中读取 TabGroup 的方法,所以需要在 rss 中增加如下的描述: RESOURCE EIK_APP_INFO { status_pane = r_app_status_pane; } RESOURCE STATUS_PANE_APP_MODEL r_app_status_pane { panes= { SPANE_PANE { id = EEikStatusPaneUidNavi; type = EAknCtNaviPane; resource = r_navi_decorator; } }; } RESOURCE NAVI_DECORATOR r_navi_decorator { type = ENaviDecoratorControlTabGroup; control = TAB_GROUP { tab_width = EAknTabWidthWithTwoTabs; active = 0; tabs = { TAB { id = EUniNewsTabSZ; txt = qtn_tab_title_sz;

37

Symbian 学习笔记 }, TAB { id = EUniNewsTabGJ; txt = qtn_tab_title_gj; } }; }; } 在代码中设置了 TabGroup 的观察者是 this, 说明当前的 AppUi 应该实现接口 MAknTabObserver 的方 法 void TabChangedL(TInt aIndex). 不过,我觉得 TabGroup 应该是动态生成的比较常用,所以可以不用在 rss 中描述这些东西,在代码中去 创建就好了: iTabGroup = CAknTabGroup::NewL(*iNaviPane); iTabGroup->SetTabFixedWidthL(EAknTabWidthWithTwoTabs); iTabGroup->SetObserver(this); RArray<TNewsChannel> *rc=iChannelHandler->GetChannels(); for(TInt i=0;i<rc->Count();i++){ iTabGroup->AddTabL((*rc)[i].id,*((*rc)[i].title)); }

iDecoratedTabGroup=CAknNavigationDecorator::NewL( iNaviPane, iTabGroup, CAknNaviga tionDecorator::ETabGroup ); iNaviPane->PushL( *iDecoratedTabGroup ); iTabGroup->SetActiveTabByIndex(0); 这样就 OK 了.

Symbian 学习笔记(13):让程序随系统启动的尝试

在论坛上看到这方面的几个帖子,想到已有好几天没写博了,最近在折腾MTK的东西,累啊.今天就抽空 研究一下如何在S60 第三版中做一个自启动应用吧. 参考这篇文章:How to autostart an application on boot up in 3rd- Startup List Management APIhttp://wiki.forum.nokia.com/index.php/How_to_autostart_an_application_on_boot_up_ in_3rd-_Startup_List_Management_API

38

Symbian 学习笔记 第一步: 写一个RSS文件, 就用自己的那个UID作为文件名即可, 例如我的UniNews_0xE94DA878.exe, 它的UID就是 0xE94DA878,所以我作一个E94DA878.rss文件放在data目录下面,内容如下: #include <startupitem.rh> RESOURCE STARTUP_ITEM_INFO startexe { executable_name = "!:\sys\bin\UniNews_0xE94DA878.exe"; recovery = EStartupItemExPolicyNone; } 第二步:修改 MMP,加一个资源如下: START RESOURCE E94DA878.rss TARGET E94DA878.rsc TARGETPATH resource\apps END //RESOURCE 意思就是说将上面那个 rss 编译成同名的 rsc 放在目录 resource\apps 下面. 第三步:上面弄完了基本就可以了但是我在模拟器上没有跑起来,所以还是得放到真机上尝试,因此再改 一下那个 PKG 文件吧,如下: "$(EPOCROOT)Epoc32\data\z\resource\apps\E94DA878.rsc" -"!:private\101f875a\import\[ E94DA878].rsc" 就是将生成的 rsc 拷入手机的相应目录下即可. 就这么要改动的地方,相当简单吧.随便拿个程序按上述方法改一下就可以了. 不过,最关键的一点是,自签名的 SISX 是不支持的!!!切记!! 原因是: Startup List Management API does not work with Self-Signed application. 所以,我们还需要去 https://www.symbiansigned.com/app/page 申请一个 Open Signed ,按界 面上操作即可,选中所有能力,一会它会发一个邮件到你的邮箱中,确认一下,它再将生成的 sisx 文件下 载地址发给你,就可以点击下载了,将这个 sisx 安装到手机里,重启手机,不错,我们的应用真得自己跑 起来了.

Symbian 学习笔记(14):使用 Browser Control API

再把这个 Browser Control API 也总结一下吧,只是做个引导,其实要掌握它的用法最好的方法是打开 9.1\S60_3rd\S60Ex\BrCtlSampleApp 这个例子来阅读,它几乎涵盖了这个 API 的所有使用方法. 而我在 UniNews 中只使用了它最基本的用法,下面给出代码: 首先,在 H 文件中声明一个控件成员: 39

Symbian 学习笔记 #include <coecntrl.h> #include <brctlinterface.h> #include <brctldefs.h> #include <brctllayoutobserver.h> #include <brctllinkresolver.h>

class CUniNewsWebContainer : public CCoeControl, MCoeControlObserver,MBrCtlLoadEven tObserver { public: // Constructors and destructor ~CUniNewsWebContainer(); static CUniNewsWebContainer* NewL(const TRect& aRect); static CUniNewsWebContainer* NewLC(const TRect& aRect); private: // New functions void ConstructL(const TRect& aRect); CUniNewsWebContainer(); public: // Functions from base classes TKeyResponse OfferKeyEventL(const TKeyEvent& aKeyEvent, TEventCode aType); void HandleBrowserLoadEventL(TBrCtlDefs::TBrCtlLoadEvent aLoadEvent,TUint aSize, TUint16 aTransactionId); void LoadContentL(TInt id); private: // Functions from base classes void SizeChanged(); TInt CountComponentControls() const; CCoeControl* ComponentControl(TInt aIndex) const; void Draw(const TRect& aRect) const; void HandleControlEventL(CCoeControl* aControl, TCoeEvent aEventType); HBufC8* ReadFileLC(const TDesC& aFileName); private: //data CBrCtlInterface* TUint TInt }; 主要声明了三个成员,其中 CBrCtlInterface 是主要的 browser 控件,其它两个是构造时的所需要的参 数.而这个类派生于接口 MBrCtlLoadEventObserver,所以实现它的方法 void iBrowser; iCapabilities; iCommandBase;

40

Symbian 学习笔记 HandleBrowserLoadEventL(TBrCtlDefs::TBrCtlLoadEvent aLoadEvent,TUint aSize,TUint16 aTransactionId); 在实现文件 CPP 中,我们需要构造它: void CUniNewsWebContainer::ConstructL(const TRect& aRect) { // Create a window for this application view CreateWindowL(); SetRect(aRect); //add your code here ... iBrowser=CreateBrowserControlL(this ,aRect ,iCapabilities ,iCommandBase ,NULL ,NULL ,NULL ,NULL ,NULL ); iBrowser->ActivateL(); if(iBrowser){ iBrowser->AddLoadEventObserverL(this); iBrowser->SetBrowserSettingL(TBrCtlDefs::ESettingsFontSize,TBrCtlDefs::EFontSiz eLevelNormal); //softkey observer //link resolver //special load observer //layout observer //dialog provider

} ActivateL(); } 在构造函数中我们初始化那两个参数: CUniNewsWebContainer::CUniNewsWebContainer() { // No implementation required iCapabilities=TBrCtlDefs::ECapabilityDisplayScrollBar|TBrCtlDefs::ECapabilityLoadHttp Fw; iCommandBase=TBrCtlDefs::ECommandIdBase; iBrowser=NULL; } 删除的时候记得将它的事件监听器都注销掉: CUniNewsWebContainer::~CUniNewsWebContainer() { // No implementation required if(iBrowser){

41

Symbian 学习笔记 iBrowser->RemoveLoadEventObserver(this); } delete iBrowser; iBrowser=NULL; } 此外,它跟其它控件一样,在 Resize 时要处理一下,并且它也需要声明自己是一个组件等等的. 而方法 HandleBrowserLoadEventL 只需要简单地重绘一下即可. 真正的使用在这儿呢,很简单: void CUniNewsWebContainer::LoadContentL(TInt id) { if(iBrowser){ TFileName fname; fname.Format(KContentFile,id); iBrowser->LoadUrlL(fname); } } 就是一句话 LoadUrlL 就可以了,这个 URL 可以是 http:// 也可以是 file://,很方便. 不过经常我们是需要将内存里的内容加载显示出来,那就稍稍多做一点工作: void CUniNewsWebContainer::LoadContentL(TInt id) { if(iBrowser){ TFileName fname; fname.Format(KContentFile,id); HBufC8 * buf=ReadFileLC(fname); _LIT(KURL,"data:%d"); TBuf<32> url; url.Format(KURL,id); _LIT8(KDataType, "text/html"); TDataType dataType(KDataType()); TUid uid; uid.iUid = KCharacterSetIdentifierUtf8; iBrowser->LoadDataL(url,*buf,dataType,uid); CleanupStack::PopAndDestroy(); } }

42

Symbian 学习笔记 这里的 URL 用 data:// 开头主要是用于历史记录作个标签罢了.而内容格式是 text/html,不过要换成 TDataType 类型.而字符集使用 UTF8. 我试了一下,觉得加载到内存再显示的效果比直接加载文件要快(主要是指切换页面时). 另外,这个控件有个 BUG,在退出时会有内存泄露,按网上的说法,在构造后激活它即可,但是我试了也 没有效果?!

Symbian 学习笔记(15) - 解析 XML 文件(上)

我曾在前面介绍过一个 可用于BREW环境下的XML Parser,今天想分享的是如何在Symbian平台上解析 XML文件,不需要第三方的东西,Symbian已经为我们提供了这个类CParser. 网上也有这方面的资料,建议参考: http://wiki.forum.nokia.com/index.php/How_to_parse_XML_file_using_CParser_class 不过,要注意的是Symbian中的CParser是基于SAX方式来解析的也就是说它是基于事件流方式,对于 SAX,如果做过JAVA开发的一般不会陌生了.与DOM相比SAX方式在操作上会有点麻烦而且显得没那么 好理解. 简要比较一下吧,DOM是将XML在内存中展开成一个树的模型,我们可以方便地访问它的每个子节点,可 读可写.但是SAX呢?我们只能通过一个单向文本流去解析XML,在过程中有多个事件回调(开始某个元 素处理,结束某个元素处理等等),它是单向只读的. 下面我们来详细说明一下如何实现吧. 首先,我们建立一个解析器派生于CActive,由它负责整个解析过程(因为这是一个异步操作). #include <xmlparser.h> #include <xmlcontenthandler.h> using namespace Xml; class MXMLHandlerObserver { public: virtual void OnParseCompleted( TInt aError ) = 0; }; class CXMLActiveParser : public CActive { public: ~CXMLActiveParser(); static CXMLActiveParser* NewL(MXMLHandlerObserver& aObserver,MContentHandler& aHandler); 43

Symbian 学习笔记 static CXMLActiveParser* NewLC(MXMLHandlerObserver& aObserver,MContentHandler & aHandler); public: void StartL(const TDesC& aFileName); private: CXMLActiveParser(MXMLHandlerObserver& aObserver,MContentHandler& aHandler); void ConstructL(); private: void RunL(); void DoCancel(); TInt RunError(TInt aError); private: CParser* HBufC8* RFile iParser; iBuffer; iFile; *iHandler;

MContentHandler

MXMLHandlerObserver *iObserver; RFs }; 除了CActive所必需的东西以外, 我们增加了 iParser 成员负责解析, iBuffer保存文件内容以供给iParser 去解析,而iHandler是SAX所特有的回调处理类(后面详述),iObserver则是自定义的一个接口,其实 是一个Notifer,就是在解析完成后调用它的OnParseCompleted方法. 实现的部分主要功能在Construct,Start和Run三个函数中: void CXMLActiveParser::ConstructL() { CActiveScheduler::Add( this); // Add to scheduler iParser = CParser::NewL( KXmlMimeType, *iHandler ); iFs.Connect(); } void CXMLActiveParser::StartL(const TDesC& aFileName) { Cancel(); User::LeaveIfError( iFile.Open( /*CCoeEnv::Static()->FsSession()*/iFs, aFileName, EFileRead ) ); delete iBuffer; iFs;

44

Symbian 学习笔记 iBuffer = 0; iBuffer = HBufC8::NewL( KFileBufferSize ); TPtr8 bufferPtr( iBuffer->Des() ); iFile.Read( bufferPtr, KFileBufferSize, iStatus ); SetActive(); iParser->ParseBeginL(); } void CXMLActiveParser::RunL() { if ( KErrNone == iStatus.Int() ){ if ( iBuffer->Length() == 0){ iParser->ParseEndL(); iFile.Close(); delete iBuffer; iBuffer = 0; iObserver->OnParseCompleted(KErrNone); } else { iParser->ParseL( *iBuffer ); TPtr8 bufferPtr( iBuffer->Des() ); iFile.Read( bufferPtr, KFileBufferSize, iStatus ); SetActive(); } } else { //error handler. iObserver->OnParseCompleted(iStatus.Int()); } } 注意 CParser 在 NewL 时告诉它文档类型是 _LIT8( KXmlMimeType, "text/xml" ) ,以及它需要的 回调处理器是 iHandler. 然后在 StartL 时读入 XML 文件, 准备解析. RunL 中如果未完成则开始解析, 在 真到完成后则调用 iObserver 的 onParseCompleted 通知观察者"我处理完了,请拿走结果吧".

Symbian 学习笔记(16) - 解析 XML 文件(下)

书接上回,这篇介绍那个 MContentHandler 的实现,这是 SAX 解析方法的核心所在.

45

Symbian 学习笔记 先看看我要解析的 XML 文件如下所示,其实很简单,因为它除了 Element 和 Attribute 以外没有其它东 西了. <?xml version="1.0" encoding="utf-8" ?> <channels> <channel id="10" title="时政" > <content id="1001" title="广东牛奶中毒事件污染源调查结果 1 周后公布"/> <content id="1002" title="河南淅川公安局因儿童被拐案设'局耻日'"/> <content id="1003" title="深圳大学 135 名师生感染病毒引发腹泻"/> </channel> <channel id="11" title="国际"> <content id="1101" title="巴以将于 4 月 7 日恢复领导人级和谈"/> <content id="1102" title="古巴解除长期禁令允许国民入住涉外酒店"/> <content id="1103" title="联合国决定继续对刚果(金)实行武器禁运"/> <content id="1104" title="俄拒绝接受美国进攻性战略武器问题建议"/> </channel> <channel id="12" title="财经"> <content id="1201" title="大飞机公司拟定名中国商用飞机有限公司"/> <content id="1202" title="大部制新部委定编制方案 6 月底前上报"/> </channel> </channels> 我们的解析处理器的声明如下: #include <xmlcontenthandler.h> #include <xmldocumentparameters.h> using namespace Xml; class TNewsChannel { public: TInt id; HBufC16 * title; }; class TNewsContent { public: TInt id; TInt pid; HBufC16 * title; }; class CChannelXmlHandler : public MContentHandler { public:

46

Symbian 学习笔记 // Constructors and destructor ~CChannelXmlHandler(); static CChannelXmlHandler* NewL(); static CChannelXmlHandler* NewLC(); RArray<TNewsChannel>* GetChannels(); RArray<TNewsContent>* GetContents(); TInt TInt private: CChannelXmlHandler(); void ConstructL(); private: // from MContentHandler void OnStartDocumentL( const RDocumentParameters &aDocParam, TInt aErrorCode ); void OnEndDocumentL( TInt aErrorCode ); void OnStartElementL( const RTagInfo &aElement, const RAttributeArray &aAttributes, TInt aErrorCode ); void OnEndElementL( const RTagInfo &aElement, TInt aErrorCode ); void OnContentL( const TDesC8 &aBytes, TInt aErrorCode ); // ... ... private: TInt iCurPID; RArray<TNewsChannel> iChannels; RArray<TNewsContent> iContents; }; 大多数是MContentHandler所声明的方法,这就是SAX事件解析模式的关键了,我们只需要在这些方法 中做相应的处理即可. 除此之外,iChannels和iContents是我们定义了用来保存解析结果的成员,它的类型是RArray,关于 RArray可以参考我的别一篇笔记: http://blog.csdn.net/sharetop/archive/2008/03/21/2203450.aspx GetContent(TInt pid,TInt index); ContentCount(TInt pid);

47

Symbian 学习笔记

因为我们的XML比较简单,所以在CPP中只要处理OnStartElementL就可以了: void CChannelXmlHandler::OnStartElementL( const Xml::RTagInfo &aElement, const Xml::RAttributeArray &aAttributes, TInt aErrorCode ) { if(aElement.LocalName().DesC().Compare(KChannelName)==0){ TNewsChannel chn; for(TInt i=0;i<aAttributes.Count();i++){ if(aAttributes[i].Attribute().LocalName().DesC().Compare(KTitleName)==0){ chn.title=CnvUtfConverter::ConvertToUnicodeFromUtf8L(aAttributes[i].Value( ).DesC()); } else if(aAttributes[i].Attribute().LocalName().DesC().Compare(KIdName)==0){ TLex8 lex; lex.Assign(aAttributes[i].Value().DesC()); lex.Val(chn.id); } } iChannels.Append(chn); iCurPID=chn.id; } else if(aElement.LocalName().DesC().Compare(KContentName)==0){ TNewsContent cnt; cnt.pid=iCurPID; for(TInt i=0;i<aAttributes.Count();i++){ if(aAttributes[i].Attribute().LocalName().DesC().Compare(KIdName)==0){ TLex8 lex; lex.Assign(aAttributes[i].Value().DesC()); lex.Val(cnt.id); } else if(aAttributes[i].Attribute().LocalName().DesC().Compare(KTitleName)==0) { cnt.title=CnvUtfConverter::ConvertToUnicodeFromUtf8L(aAttributes[i].Value() .DesC()); } } iContents.Append(cnt); } } 这个回调会在解析器遇到元素头时进入,然后我们就可以根据传入的参数取到当前元素的信息,如元素名 称,属性值等,将它们保存在我们定义的数据成员中以备将来使用即可. 在使用这个解析器的地方,比如我们的 AppView 负责解析 XML 文件,那它应该包含一个

48

Symbian 学习笔记 MContentHandler 的成员,并且它实现接口 MXMLHandlerObserver. 于是,这样启动解析过程: iChannelHandler=CChannelXmlHandler::NewL(); iXmlParser=CXMLActiveParser::NewL(*this,*iChannelHandler); iXmlParser->StartL(KChannelXMLFile); 然后在它的 OnParseCompleted 方法中去 iChannelHandler 中取出解析结果,展示出来或者随便怎么 用了.

Symbian 学习笔记(17) - 初探 WebServices API 的使用(上)

很久没有学习 Symbian 了, 今天研究一下如何使用 Symbian 中提供的 WebService 框架来 SayHello. 从 SDK 文档中提供的资料来看这个接口似乎有点复杂,包括了 Connection API, Description API 和 Manager API 三套东西,此外还涉到了 XML 的解析之类的一些 API 的应用. 阅读了一下它的例子程序(S60Ex 目录下的 AddressBook),让我更晕乎了.怎么 跟自己平时使用的 WebService 不一样了? 在 SDK 文档中关于 CSenServiceConnection 有这么一段描述: Web Services 包括两种不同的框架模型: 1. Identity Based Web Services Framework (ID-WSF). The framework ID for this is KDefaultIdWsfFrameworkID ("ID-WSF"). 2. Basic Web Services Framework. Framework ID is KDefaultBasicWebServicesFrameworkID ("WS-I"). 如果提供了 Contract 则缺省使用 ID-WSF. 首先用.NET 做一个简单的 WebServices 来测试,就用缺省产生的 HelloWorld 吧. 很简单的,它的 SOAP 描述如下:
view plaincopy to clipboardprint?

1. 2. 3. 4. 5. 6.

<pre class="csharp" name="code">POST /uim/PService.asmx HTTP/1.1

Host: localhost

49

Symbian 学习笔记

7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" x mlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.x mlsoap.org/soap/envelope/"> 30. 31. 32. 33. 34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. </soap:Envelope> </soap:Body> <HelloWorld xmlns="http://sharetop/pservice" /> <soap:Body> <?xml version="1.0" encoding="utf-8"?> SOAPAction: "urn:pservice:helloworld" Content-Length: length Content-Type: text/xml; charset=utf-8

50

Symbian 学习笔记

49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. 71. <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" x mlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:soap="http://schemas.x mlsoap.org/soap/envelope/"> 72. 73. 74. 75. 76. 77. 78. 79. 80. 81. 82. 83. 84. 85. 86. 87. 88. 89. 90. </HelloWorldResponse> <HelloWorldResult>string</HelloWorldResult> <HelloWorldResponse xmlns="http://sharetop/pservice"> <soap:Body> <?xml version="1.0" encoding="utf-8"?> Content-Length: length Content-Type: text/xml; charset=utf-8 HTTP/1.1 200 OK

51

Symbian 学习笔记

91. 92. 93. 94. 95.

</soap:Body>

</soap:Envelope></pre>

下面我们自己来做一个 WS 的客户端实例吧.先用向导生成一个 HelloWorld 应用, 为了研究方便,我们不打算做什么界面,所有的输出都通过 LOG 输出到日志文件. 为了编码方便,我们增加一个类 WebEngine,它应该派生于 CSenBaseFragment 和 MSenServiceConsumer.声明如下:
view plaincopy to clipboardprint?

1.

class CWebEngine : public CSenBaseFragment, public MSenServiceConsumer

2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. or); virtual void HandleErrorL(const TInt aErrorCode,const TDesC8& aErr virtual void HandleMessageL(const TDesC8& aMessage); //from MSenServiceConsumer void SayHello(); void ConnectL(); static CWebEngine* NewLC(); static CWebEngine* NewL(); ~CWebEngine(); public: {

52

Symbian 学习笔记

30. 31. 32. 33. 34. 35. 36. 37. 38. 39. virtual void StartElementL(const TDesC8& aNsUri, const TDesC8& aLo calName, const TDesC8& aQName, const Xml::RAttributeArray& aAttrs); //from CSenBaseFragment protected: virtual void SetStatus(const TInt aStatus);

40. 41. virtual void EndElementL(const TDesC8& aNsUri, alName, const TDesC8& aQName); 42. 43. 44. 45. 46. 47. 48. 49. 50. 51. 52. 53. 54. 55. 56. 57. 58. 59. 60. 61. 62. 63. 64. 65. 66. 67. 68. 69. 70. CSenXmlReader* iXmlReader; CSenXmlServiceDescription* iSession; CSenServiceConnection* iConnection; private: CHelloWorldResult * delegate; public: void ConstructL(); CWebEngine(); private: const TDesC8& aLoc

53

Symbian 学习笔记

71. 72. 73. };

除了实现两个父类的方法以外,还要增加 ConnectL()用来连接,SayHello()用来调 用远程方法.那个 delegate 是一个 CHelloWorldResult 类的实例,这个类同样派生于 CSenDomFragment,说明它对应一段 XML 内容,我们用它来处理结果,就是那个 HelloWorldResponse 标签下的内容. 这个 WebEngine 的实现逻辑是: 先在 ConnectL 中初始化 WS 客户端, SetStatus 在 回调中取当前状态值如果为 KSenConnectionStatusReady ,则可以调用 SayHello 去 执行那个 WS 的方法,然后,在 HandleMessageL 回调中将得到的结果(XML 内容的字 节流)去解析一下,解析 XML 的回调就是那两个 StartElement 和 EndElement. 附:这段代码太长了,分节吧.现在 CSDN 也不知怎么搞的,帖代码如此难看,唉. 无语.

Symbian 学习笔记(18) - 初探 Web Services API 的使用(中)

继续刚才的,现在来看具体代码,先是 ConnectL 的实现:
view plaincopy to clipboardprint?

1. 2. 3. 4. 5.

void CWebEngine::ConnectL()

{

CSenXmlServiceDescription* pattern = CSenXmlServiceDescription ::NewLC();

6. 7. pattern->SetFrameworkIdL(KDefaultBasicWebServicesFrameworkID);

8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 54 CleanupStack::PopAndDestroy(pattern); iConnection = CSenServiceConnection::NewL(*this, *pattern); iConnection = NULL; delete iConnection; pattern->SetEndPointL(KWSEndPoint);

Symbian 学习笔记

19.

}

这里注意一点与那个AddressBook例子不同的是我们声明了不同框架类型是 KDefaultBasicWebServicesFrameworkID,并且这样只需要提供EndPoint而不需要 Contract了.KWSEndPoint的值是在CPP前声明了: _LIT8(KWSEndPoint,"http://192.168.0.201/uim/PService.asmx"); CSenServiceConnection::NewL 的两个参数,一是自己(即 MSenServiceConsumer)负责处理回调,二是一个 CSenXmlServiceDescription 负责 参数配置. 在回调 SetStatus 中我只是简单地打印出状态值. 再看那个 SayHello 的实现吧,在这个函数中要负责封装 SOAP 消息包,这时我才遇 到了使用 Symbian 的 WebServiceAPI 烦人的问题:原来这个 SOAP 包要自己封装啊! 同样 SOAP 的结果也要自己去解析!!
view plaincopy to clipboardprint?

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26.

void CWebEngine::SayHello()

{

if(iConnectionState==1){

//send

CSenSoapEnvelope

*env = CSenSoapEnvelope::NewL();

CleanupStack::PushL(env);

env->SetSoapActionL(KWSContract);

env->BodyL().AddElementL(KWSNamespace,KWSHelloworld);

iConnection->SendL(*env);

CleanupStack::PopAndDestroy(env);

}

55

Symbian 学习笔记

27.

}

好在 HelloWorld 不需要参数,所以这个 SOAP 请求还算简单,注意这个 SetSoapActionL 函数它的 KWSContract 就是那个"urn:pservice:helloworld" (见上 篇中的 SOAP 请求描述) 因为 CSenSoapEnvelope 同样派生于 CSenBaseFragment , . 所以它的 Body 也可以增加下级节点,上面的代码很好理解. 一旦调用了 iConnection->SendL 以后, 手机会弹出选择接入点, 说明这里开始连接 网络了,得到结果后,我们回调 HandleMessageL 中处理结果.
view plaincopy to clipboardprint?

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13.

void CWebEngine::HandleMessageL(const TDesC8& aMessage)

{

RDebug::Printf("===================HandleMessageL");

LOG_ALL(aMessage);

SetReader(*iXmlReader);

ParseL(aMessage);

}

这里我们将得到的结果(完整的 SOAP 响应的 XML 内容)交给 iXmlReader 去解析, 于是此时又会涉及到另两个回调 StartElement 和 EndElement.注意这里补充一下 iXmlReader 的初始化在 WebEngine 的 ConstructL 中完成:
view plaincopy to clipboardprint?

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11.

void CWebEngine::ConstructL()

{

LOG_OPEN();

CSenBaseFragment::BaseConstructL(KQueryResponseLocalName);

iXmlReader = CSenXmlReader::NewL();

}

56

Symbian 学习笔记

两句话: 一是因为自己是派生于 CSenBaseFragment, 所以先调用 BaseConstructL 构造一下自己是一个 HelloWorldResponse 标签的 XML 节点.二是构造出 iXmlReader 实例. 下面继续说解析 XML 的回调处理:
view plaincopy to clipboardprint?

1.

void CWebEngine::StartElementL(const TDesC8& aNsUri, aLocalName,

const TDesC8&

const TDesC8& aQName, const Xml::RAttributeArray& aAttrs)

2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. void CWebEngine::EndElementL(const TDesC8& aNsUri, alName, const TDesC8& aQName) 32. 33. 34. { const TDesC8& aLoc } } CleanupStack::Pop(delegate); DelegateParsingL(*delegate); CleanupStack::PushL(delegate); delegate = CHelloWorldResult::NewL(aNsUri,aLocalName,aQName); if(aLocalName==KQueryResponseLocalName){ LOG_FORMAT((KFmt,aLocalName)); _LIT(KFmt,"StartElement (%s)"); RDebug::Printf("================StartElement"); {

57

Symbian 学习笔记

35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45.

RDebug::Printf("==================EndElement");

_LIT(KFmt,"EndElement (%s)");

LOG_FORMAT((KFmt,aLocalName));

CSenBaseFragment::EndElementL(aNsUri, aLocalName, aQName);

}

这个 EndElement 没啥好说的,就是调一下老子的 EndElement 罢了.倒是那个 StartElement 函数它在遇到 HelloWorldResponse 标签时会交给 delegate 去处理下级 节点,就是说<HelloWorldResponse>以下的 XML 内容由 CHelloWorldResult 类的负 责处理了.

Symbian 学习笔记(19) - 初探 Web Services API 的使用(下)

继续,看看如何取出结果值,就是<HelloWorldResult>Hello World</HelloWorldResult>中的字串 HelloWorld,这个代码在 CHelloWorldResult 中:
view plaincopy to clipboardprint?

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18.

TPtrC8 CHelloWorldResult::Result()

{

CSenElement * pElement = AsElement().Element(KHelloResult);

if(pElement)

{

_LIT(STR,"result----");

LOG(STR);

return pElement->Content();

}

58

Symbian 学习笔记

19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. }

else {

LOG(_L("no result"));

return KNullDesC8();

}

其实这儿也好理解,看看 CSenElement 的 SDK 文档就知道个八九不离十了. 总结一下: 1.利用 Symbian 中的 Web Services API 实现一个 WEB 服务客户端是比较烦人的 事情,主要的麻烦点在于 SOAP 请求与响应的 XML 内容都得自己去构造和解析. 2.常用的 WebService 只有 EndPoint 没有 ID 服务,所以它应该是基础型的 WS 框 架.

Symbian 学习笔记(20) - 用 gSOAP 更简单地实现 Web Services Client

昨天折腾了一下那个 gSOAP, 发现这东西比 Symbian 自带的 WebServiceAPI 更简 单方便,推荐使用! gSOAP的官方网站是:http://gsoap2.sourceforge.net/ 具体使用方法不多说,因为网上 GOOGLE 一下很多资料.下面只对在 symbian 环 境下使用略说几点. 我下载的是最新版的 v2.7.10,下载的压缩包里就有一个 symbian 目录,里面是在 symbian 的例子,可以参考. 试着做了一个 HelloWorld,还是访问前面做的那个 web service,按照它的例子步 骤如下: 1.解开软件压缩包. 2.根据 wsdl 生成代码存根等 H/CPP 文件.在命令行进入 gsoap\bin\win32 目录, 先后执行以下指令:

59

Symbian 学习笔记

wsdl2 -s -o pservice.h http://192.168.0.201/uim/pservice.asmx?WSDL 在当前目录得到一个 pservice.h 文件. soapcpp2 -CLwx pservice.h 在当前目录得到多个源文件,每个文件的具体作用含义大家可以看官方资料. 3.在 Carbide C++中新建一个工程,我就叫它 HelloWsTwo,直接用了那个 HelloWorld 模板框架.然后将步骤 2 生成的几个文件弄过去,首先将 soapH.h/soapStub.h/soapPServiceSoapProxy.h 拷到工程的 inc 目录下,将 soapC.cpp/soapClient.cpp 拷到工程的 src 目录下. 4.还有两个文件在 gsoap 目录下,stdsoap2.h 和 stdsoap2.cpp,也拷入相应目录, 这里其实按例子中的 readme 说明是不拷的,而是在 mmp 中修改一下指过来,不过,因 为我们需要修改这个 cpp 所以方便起见,还是拷过去吧,省得跟其它工程弄混了. 5.先修改 HelloWsTwoUi 这个类,在头文件中增加如下代码:
view plaincopy to clipboardprint?

1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18.

// 此处省略

#include "soapPServiceSoapProxy.h"

// FORWARD DECLARATIONS

class CHelloWSTwoAppView; class PServiceSoap;

//此处省略

private:

CHelloWSTwoAppView* iAppView;

PServiceSoap * iService;

//此处省略

引入那个代理头文件,然后加一个 PServiceSoap 类型的成员变量 iService.接着修改 cpp 文件,在构 造时 new 它,在析构时 delete 它即可.
view plaincopy to clipboardprint?

1.

case ECommand1:

60

Symbian 学习笔记

2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22.

{

//webservice

if(iService){

_ns1__HelloWorldResponse resp;

if(iService->__ns2__HelloWorld(NULL,&resp)==SOAP_OK){

RDebug::Printf(resp.HelloWorldResult);

}

}

}

break;

真正的工作过程相当简单, 就是先声明一个返回值_ns1_HelloWorldResponse 类型 的变量 resp,调用 iService 的成员函数__ns2__HelloWorld 即可.第一个参数是 endpoint,可以为 NULL,因为 stub 里已经有了,第二个参数就是 resp 的引用. 6.现在还要修改 mmp 文件,carbide 是会自动帮你加上几个源文件的,接受即可. 此外,mmp 中更需要增加一个包含目录 include\libc,以及几个链接库,如下: SYSTEMINCLUDE \Epoc32\include\libc LIBRARY eexe.lib estlib.lib ecrt0.lib CAPABILITY ReadUserData NetworkServices nostrictdef 7.就这么容易,但是编译时,不行,出错了!报 undefined _soap_outLONG64 之 类的错误!! 这时开始就折腾了我差不多一天.其实修改也挺简单的,打开那个 stdsoap2.cpp, 我们发现其实这个函数是有的,只是被一个宏 WITH_LEAN 给关掉了,所以打开即可. 但 是打开宏,错误更多了,没办法,只能将这个函数定义前的宏注释掉看看,两个错变成三个 错了,硬着头皮继续,在 stdsoap2.cpp 中注掉多个#ifndef WITH_LEAN 的条件,终于 不再报错了. 一切运行也就正常了.这个错误实在是够变态的了吧.

61

Symbian 学习笔记

总结一下: 1.建议将 stdsoap2.*弄到工程里去,因为改了源代码应该会与 PC 平台 的应用相冲突,当然假如你并不打算将 gSOAP 用于 symbian 以外的场合,可以仍放在 gsoap 目录下. 2.用 gsoap 最大的好处是不用自己去解析那个 soap 响应,也不用自己去封装 soap 请求了.

Symbian 学习笔记(21) - 原来还有这个工具 wsdl2cpp,访问 webservice 也很简单

在论坛里看到有人问使用 wsdl2cpp 生成代码,才知道原来 symbian 提供了一个辅 助工具来生成 webservice client 的代码,比起我上回折腾的自己参考 AddressBook 例 子做的 webservice 客户端要简单多了,类似于 gSOAP 的使用. 首先, forum.nokia.com.cn 搜一下 WSDL-to-C++_for_S60, 去 这是用于 Carbice Vs 的工具,不过也可以独立安装,用命令行来生成所需要的代码.下载安装即可. 第二步,运行它的 wsdl2cpp 工具,得到一些源代码,不过它比 gSOAP 弱智一点点, 不能直接给出一个 wsdl 的 URL,所以最好将 wsdl 下载到本地,给它一个文件名即可: wsdl2cpp --callback-class PServiceConsumer --include-dir .\work\inc --source-dir .\work\src pservice.wsdl 这样我们会在当前目录下的 work/src 和 work/inc 下得到它生成的头与源文件, 实现 的原理仍是用的 Symbian 内置的 Web Service API 来做的.将这些代码弄到工程里去. 第三步,工程里新增了代码,修改 mmp,与直接使用这些 API 一样,总之,这个工 具只是帮助我们生成了源码框架,比较方便罢了.不过额外有一点增加的地方是: LIBRARY XmlDataBinding.lib

USERINCLUDE \epoc32\include\libc USERINCLUDE \epoc32\include\xmldatabinding 这里的 xmldatabinding 是新的东西,所以之前需要安装,在安装 wsdl-to-c++工 具后,有一个 XmlDataBinding.zip 在它的目录下,打开解到相应的 epoc32 目录里去即 可.它包括了 lib 和一些头文件. 并且,在手机跑,应该将 XmlDataBinding.sis 也安装一下,从这一点看,似乎比 gSOAP 要麻烦一点了. 第四步,现在开始动手修改代码,很简单了.
view plaincopy to clipboardprint?

1.

private: 62

Symbian 学习笔记

2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. CPServiceConsumer * iObserver; CPServiceService * iService; CHelloWSThreeAppView* iAppView; // Data

在头文件里增加 iService 和 iObserver 就可以了,CPP 中有两步,一是初始化连接, 二是远程调用方法:
view plaincopy to clipboardprint?

1. 2. 3.

//这里初始化代码

CSenXmlServiceDescription *pServiceDesc = CSenXmlServiceDescription::N ewLC(KServiceEndpoint, KNullDesC8());

4. 5. kID); 6. 7. 8. 9. 10. 11. iService = CPServiceService::NewLC(*iObserver,*pServiceDesc); iObserver = CPServiceConsumer::NewLC(); pServiceDesc->SetFrameworkIdL(KDefaultBasicWebServicesFramewor

12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. //这里远程调用代码 LOG(KStr); _LIT(KStr,"Init service.");

63

Symbian 学习笔记

25. 26. 27. 28. 29. 30. 31. 32. 33.

if(iObserver->iStatus==KSenConnectionStatusReady){

RHelloWorld aHello;

RHelloWorldResponse aHelloResp;

TRAPD(error,aHelloResp=iService->HelloWorldL(aHello));

34. 35. 36. 37. 38. 39. 40. 41. 42. 43. 44. 45. 46. 47. 48. 49. } LOG_FORMAT((KStr,buf)); _LIT(KStr,"result is %S"); buf.Copy(aHelloResp.iHelloWorldResult); TBuf<64> buf; } RDebug::Printf("error %d",error); if(error){

这样就好了,当然析构时要删除这些成员变量. 总结一下,其实我用三种方法来调用 web service,都是最简单的 HelloWorld,比 较而言,还是 gSOAP 比较方便.

Symbian 学习笔记(22) - 关于皮肤的小结

在 Symbian 中使用皮肤是一个让我这样新手颇觉得费劲的事情.折腾了一天,把几 种情况都试验了一把,总结一下吧. 1.一句话的方法 最基本的一招就是在 AppUi 中的 ConstructL()中加一句话搞定.如下:
view plaincopy to clipboardprint?

64

Symbian 学习笔记

1.

void CTestMIMAppUi::ConstructL() {<br> BaseConstructL(CAknAppUi::EAknE nableSkin);<br>//add your code here...<br>}<br>

用上面这句话基本上能让大部分控件的透明化,显示出系统的皮肤. 但是,有时我们会发现部分控件(比如那个 CEikEdwin)仍显示的一个难看的白底, 此时,我们需要做一些额外的工作了. 修改 Container 的头文件,增加一个成员变量:
view plaincopy to clipboardprint?

1.

CAknsBasicBackgroundControlContext* iBgContext;

然后在对应的 ConstructL 函数中初始它:
view plaincopy to clipboardprint?

1.

iBgContext = CAknsBasicBackgroundControlContext::NewL(KAknsIIDQsnBgAre aMainIdle,aRect,ETrue);

这儿的 KAknsIIDQsBgAreaMainIdle 你可以选择其它的,不碍事的. 然后,因为 CEidEdwin 有一个很方便的成员方法 SetSkinBackgroundControlContextL,所以接下来 的代码就简单了:
view plaincopy to clipboardprint?

1.

iEdWin=new(ELeave)CEikEdwin;<br> CleanupStack::PushL(iEdWin);<br> iEdW in->SetContainerWindowL(*this); <br> iEdWin->ConstructL();<br> iEdWin->S etSkinBackgroundControlContextL(iBgContext);<br> iEdWin->SetExtentToWhol eScreen();<br> iEdWin->SetFocus(ETrue);<br> iEdWin->ActivateL();<br> Cle anupStack::Pop(iEdWin);

这样就可以了.别忘了,在析构时 delete 它. 2.终极方法显示系统皮肤 再进一步,如果控件没有这么方便的成员让我们去设置它的背景,也有办法(参考 http://www.newlc.com/Enable-Skin-support-in-your.html). 很好办,先在 H 文件中增加一个 MopSupplyObject 的声明:
view plaincopy to clipboardprint?

1.

TTypeUid::Ptr MopSupplyObject(TTypeUid aId);

然后实现中, ContructL 中就不用 iEdWin->SetSkinBackgroundControlContextL 了,而是在三个函数中分别处理:
view plaincopy to clipboardprint?

65

Symbian 学习笔记

1.

void CTestMIMEdtContainer::Draw(const TRect& aRect) const {<br> CWindo wGc& gc = SystemGc();<br><br> MAknsSkinInstance* skin = AknsUtils::SkinI nstance();<br> MAknsControlContext* cc = AknsDrawUtils::ControlContext( this );<br> AknsDrawUtils::Background( skin, cc, this, gc, aRect );<br>} <br><br>void CTestMIMEdtContainer::SizeChanged() {<br> if(iBgContext)<br > {<br> iBgContext->SetRect(Rect());<br> {<br> if ( &Windo iBgContext->SetP }<br> } <br

w() )<br>

arentPos( PositionRelativeToScreen() );<br>

> DrawNow();<br>}<br>TTypeUid::Ptr CTestMIMEdtContainer::MopSupplyObject (TTypeUid aId)<br>{<br> if (iBgContext )<br> {<br> return MAknsContr }<br> return CCoeCo

olContext::SupplyMopObject( aId, iBgContext );<br> ntrol::MopSupplyObject(aId);<br>}

这样也可以让控件透明显示出系统皮肤. 3.显示自定义皮肤 来说自定义皮肤的显示, 关键在于那个 iBgContext 成员如何弄出来, 前面的 NewL() 的第一个参数是系统定义的东西,现在我们需要自定义了. 同样,先修改一个 H 文件,增加一个成员:
view plaincopy to clipboardprint?

1.

TAknsItemID aSkinItem;

然后实现文件中的 ContructL 函数中,我们要从 MIF 文件中取图片弄成背景:
view plaincopy to clipboardprint?

1.

TFileName iMFileName;<br> iMFileName.Copy(KMifFileName);<br> CompleteW ithAppPath(iMFileName);<br> <br> aSkinItem.iMinor = 0xE2139689;<br> aSki nItem.iMajor = 1 ;<br><br> CAknsItemDef* mainBgItemDef = AknsUtils::Crea teBitmapItemDefL(aSkinItem, iMFileName, EMbmTestmimGrid);<br> AknsUtils: :SkinInstance()->SetLocalItemDefL( mainBgItemDef ); <br> iBgContext = CA knsBasicBackgroundControlContext::NewL(aSkinItem,aRect,ETrue );

这儿的 KMifFileName 是定义的资源 MIF 文件(与其它例子中加载资源图像的方法类似).

66


相关文章:
更多相关标签: