Symbian OS关于动画的绘制和处理基本上分为两大类,一类是客户端应用程序动画框架,一类是Window Server的插件动画。在介绍这两种动画前,我们先简单介绍一下精灵(sprite)的概念。
精灵(sprite)
精灵(sprite)是Window Server的概念范畴,Window Server支持两种类型的精灵,一种是常用的精灵-sprite,一种是用来作为光标(pointer cursor)。精灵是任意形状的位图,其在移动时无需程序对其后面的屏幕进行重画。Window Server之所以提供光标(pointer cursor)的支持,是因为在使用鼠标、手写笔时可以象使用一个指针设备(pointer device)一样有效。
Symbian OS中,RWsSpriteBase、RWsSprite、RWsPointerCursor这几个类实现了精灵相关的操作。下图的类图他们之间的关系:
一个精灵有一个或多个精灵成员组成,每个成员包含一个位图图片和一个现实图片的时间间隔。多个成员非别包含不同的图片按顺序已设定的时间间隔显示即可实现动画效果。RWsSpriteBase类实现了添加精灵成员、更新精灵成员等操作,RWsSprite类用来构造精灵sprite对象,RWsPointerCursor类用来构造精灵Pointer cursor对象。
应用程序使用精灵通常是以下几步:
l 用RWsSprite::Construct来构造一个RWsSprite对象。
l 用位图(CFbsBitmap对象)构造精灵成员(TSpriteMember),并设置播放时间间隔。
l 用RWsSpriteBase::AppendMember()一次添加精灵成员。
l 调用RWsSpriteBase::Activate()激活精灵。
精灵的显示位置可以在构造是制定,也可以在激活后使用RWsSprite::SetPosition()来设置。还可以通过RWsSpriteBase::UpdateMember来更新成员。Pointer Cursor的使用与sprite的类似。
插件动画(CAnim/RAnim)
这种类型的动画运行在Window Server定义的框架下。要实现这种动画效果需要实现系统定义的多态动态库,这个动态库是Window Server的插件,运行在Window Server的高优先级线程中而不是应用程序的线程中,动画的显示内容及如何显示都在这个插件里实现。同时还需要在Window Server客户端做些工作,来实现对动画插件的加载和动画控制。下面分别介绍如何实现服务端和客户端。
服务端实现
服务端的实现实际上就是动态库的实现。这个动态库必须定义构造动画的类厂,因为你可以实现窗体动画(window animation),也可以实现精灵动画(sprite animation),你可以通过这个类厂来构造你想要的动画。同时这个动态库必须在其第一个导出函数中实现构造这个类厂。系统提供了一个虚基类CAnimDll,你可以从它派生一个自己的类,实现其CreateInstanceL接口即可。下面的代码显示了如何实现这个类厂:
//DLL第一个导出函数,构造类厂
EXPORT_C CAnimDll* CreateCAnimDllL()
{
return new(ELeave) CAnimDllExample;
}
class CAnimDllExample : public CAnimDll
{
Public:
CAnim* CreateInstanceL(Tint aType)
{
CAnim *anim=NULL;
switch(aType)
{
case EKWindowAnim:
//create window animation;
anim= new (ELeave) CWindowAnimExample;
break;
case EKSpriteAnim:
//create sprite animation
anim=new (ELeave) CSpriteAnimExample;
break;
case EKFreeTimerWindowAnim:
//create freetimer animation
anim=new (ELeave) CFreeTimerWindowAnimExample;
break;
}
return anim;
}
};
上面例子中的类CWindowAnimExample、CSpriteAnimExample以及CFreeTimerWindowAnimExample,最终都是从CAnim派生而来,但不是直接从它派生。CAnim这个类是一个抽象类,它定义了被Window server调用的接口CommandReplyL 和 Command。(当客户端调用RAnim::Command 或 RAnim::CommandReply时,Window Server会自动调用CAnim::Command和CAnim::CommandReply,下面将客户端实现是会讲到这点。)客户端通过向Window Server发送命令来控制动画。下图显示了Symbian OS提供的动画类图:
上面的代码例子中的CWiundowAnimExample就是从CWindowAnim派生,CWindowAnim也是虚基类,所以你必须从其派生自己的类,并实现其定义的接口(重写其定义的纯虚函数)。CSpriteAnim、CFreeTimerWindowAnim也都是虚基类,你必须派生并实现自己的类。
注意:无论是动画的类型,还是客户端与服务端的控制命令,都是由你自己来定义。
客户端实现
对于客户端的实现,Symbian OS 提供了RAnim/RAnimDll两个类,RAnimDll类用来加载和卸载你实现的动画插件,RAnim类通过Command/CommandReply方法实现对动画的控制。在使用RAnim类操作动画之前,你必须已经使用RAnimDll成功加载了动画插件。
加载动画插件时,使用RAnimDll::Load方法,卸载时调用RAnimDll::Close方法。当Server收到RAnimDll::Load请求时,会加载指定的Dll动画插件,并调用其第一个导出函数创建动画类厂(具体到上面的例子中,即调用CAnimDll* CreateCAnimDllL()函数)。
当动画插件成功加载后就可以使用RAnim类提供的方法对动画进行操作了。需要说明的一点是RAnim也是一个抽象类,你需要从其派生自己了类,这里我们就用RAnim类来说明。
RAnim类在构造对象时,调用RAnim::Construct,这个方法有多个重载,分别用来构造窗体动画和精灵动画。当Server收到RAnim::Construct这个请求时,会调到CAnimDll:: CreateInstanceL(具体到上面例子,会调到CAnimDllExample:: CreateInstanceL),通过你传入的type参数来决定构造那类动画。
当你的动画对象也构造好后,动画就可以显示了。在动画显示过程中,你可以向Server发控制命令,如果你使用RAnim::Command,当Server收到这个请求时,会调用CAnim::Command(具体到上面例子,会调到CWindowAnimExample::Command);如果你使用RAnim:: CommandReply,当Server收到这个请求时,会调用CAnim::CommandReply(具体到上面例子,会调到CWindowAnimExample::CommandReply)。
到此为止,你就基本完成了这个动画的实现了,至于你怎么控制动画,实现什么样的动画效果,就看你怎么实现动画插件中的动画了,这完全取决于你自己。
前面介绍了Symbian OS中Window Server的插件动画,这里要介绍的是Symbian OS中另一类动画—--应用程序框架下的动画。这类动画实际上是一个提供构建动画图形应用的框架,它提供了基本的动画数据类型(如gif, svg),并将其并入客户端窗体的一般绘画操作中,或者在服务端将其作为一个精灵(sprite)。
关于这类动画,有几个关键的概念,如动画(Animation)、动画制作者(Animator)、数据提供者(Data provider)以及旁观者(Observer)。弄清楚这几个概念就基本上清楚这类动画的实现原理了。下面分别对这几个概念一一进行简单介绍。
动画(Animation)
动画(Animation)在这里的意思是客户端应用程序所拥有的一个抽象接口。这个接口独立于它所使用的数据源,动画的效果由数据提供者决定。CAnimation类定义了这个接口,Symbian OS提供了这个接口的派生类的具体实现:CBasicAnimation和CSpriteAnimation。见下图:
CBasicAnimation是一个客户端的动画类,这个类同时派生自数据提供观察者接口和动画观察者接口。这个类与window server建立一个会话,通过这个会话将动画提交到特定的窗口。
CSpriteAnimation是服务端基于精灵的动画类。由于其在服务端,被服务端控制,所以在客户端只有有限的控制权。这个类利用Window Server 的动画函数来绘制精灵动画。具体信息前面已经讲过。
CAnimationGroup类其实是一个容器,它包含一个数组,如果有多个CAnimation的类,你可以添加到这个组中,这样你可以调用这个类的Start/Stop/Resume/Pause来对所有的动画统一控制。
动画制作者(Animator)
动画制作者代表了动画框架所支持的每个数据类型。它可以处理数据解释、时序和动画控制。动画制作者对动画所有者是不可见的,但动画所有者通过一个标准接口可以对其进行控制。
动画制作者可以在客户端,也可以在服务器端。动画制作者必须能够在需要是创建型的制作者,并指定被那个动画使用。因此,动画制作者的实现一般是一个ECOM插件,在需要时被加载。每个动画可以通过从数据提供者获取的字符串来选择一个制作者插件来使用。Symbian OS 提供了CAnimator这个虚基类,要实现制作者插件,你可以从这个类派生来实现。但一般我们不需要这么做,动画框架已经在内部实现这些了。
数据提供者(Data provider)
数据提供者是一个通用的接口,用来处理不同类型的数据,这些数据具有不同的数据结构和行为,例如,GIF和SVG。数据提供者将一个动画规范(如动画文件)转化为动画制作者可识别的格式,传给动画制作者。动画通过观察者接口来访问数据提供者。动画观察者接口(MAnimationDataProviderObserver)发送事件给动画。
Symbian OS中的CAnimationDataProvider类定义了数据提供者接口。Symbian OS还提供了CICLAnimationDataProvider类,这个类从CAnimationDataProvider和MICLAnimationDataLoaderObserver派生而来,它可以解释各种数据类型,包括GIF。它以CAnimationFrame对象包含的位图为帧,以帧的形式给动画显示提供数据。
旁观者(Observer)
观察者是一个接口,负责事件的报告。动画框架提供了两个观察者,一个是数据提供者观察者(MAnimationDataProviderObserver),一种是动画观察者(MAnimationObserver,MAnimationDrawer,MAnimationTickee)。数据提供者观察者是介于动画和数据提供者之间的接口,动画使用这个观察者接口可以接受来自数据提供者的数据和事件。动画观察者是介于动画和应用程序间的接口,用来给应用程序报告事件。
实现
如果你还不能很好地理解以上的几个概念,那通过下面简单的代码段你就可以理解动画是怎么实现了。下面的代码主要说明怎么在窗体上显示GIF动画。
void CExampleAppView::ShowCAnimation(TPoint& aPosition,const TFileName &aFileName)
{
//Create CICLAnimationDataProvider object which relates to GIF file,数据提供者.
iBasicDataProvider = new (ELeave)CICLAnimationDataProvider;
iBasicDataProvider->SetFileL(CCoeEnv::Static()->FsSession(), aFileName);
//set animation config
TAnimationConfig config;
//设置循环播放动画
config.iFlags = TAnimationConfig::ELoop;
config.iData = -1;
//创建 Animation对象
iBasicAnimation = CBasicAnimation::NewL(iBasicDataProvider,aPosition,CCoeEnv::Static()->WsSession(),Window());
//启动 animation
iBasicAnimation->Start(config);
}
void CExampleAppView::Draw(const TRect& /*aRect*/) const
{
// Window graphics context
CWindowGc& gc = SystemGc();
// Area in which we shall draw
TRect drawRect = Rect();
//画动画
if(iBasicAnimation)
{
iBasicAnimation->Draw(gc);
}
}
Bitmap Animation
另外关于动画,Symbian OS还提供了Bitmap Animation,这种动画以位图为动画元素,这种动画框架在bmpanim.dll已经实现,服务端功能在bmpansrv.dll也已经实现了。在显示这种动画之前用RAnimDll来加载bmpansrv.dll即可。
这种动画由CBitmapFrameData, CBitmapAnimClientData, RBitmapAnim这几个类来完成。具体步骤如下:
l 定义一个或多个动画帧(CBitmapFrameData),每帧设置一副位图、设置播放时间间隔及位置等。
l 将定义好的帧添加到一个动画容器(CBitmapAnimClientData)。
l 创建一个动画播放对象(RBitmapAnim),并将动画容器传给这个播放对象,然后就可以播放动画。
通过下面的代码段可以看出如何来使用和处理这种动画。
void CExampleAppView::PlanAnimBitmaps()
{
//先创建一个动画容器对象
iAnimFrames=CBitmapAnimClientData::NewL();
_LIT(KBitmapAnimFileName,"C:\\Anim.mbm");
// load the mask from the mbm file
iMask=new (ELeave) CFbsBitmap;
CleanupStack::PushL(iMask);
User::LeaveIfError(iMask->Load(KBitmapAnimFileName, 0));
CleanupStack::Pop(1);
//初始化每一帧
for(TInt i=0;i<8;i++)
{
// load the image bitmap from an mbm file
CFbsBitmap* bitmap=new (ELeave) CFbsBitmap;
CleanupStack::PushL(bitmap);
User::LeaveIfError(bitmap->Load(KBitmapAnimFileName, i+1));
CBitmapFrameData* frame = CBitmapFrameData::NewL(bitmap, iMask);
//设置帧播放间隔
frame->SetInterval(1000);
//设置帧显示位置
frame->SetPosition(TPoint(50/*+i*20*/,50));
//把每一帧添加到动画容器对象中
iAnimFrames->AppendFrameL(frame);
}
CleanupStack::Pop(8);
iAnimFrames->SetPlayMode(CBitmapAnimClientData::EBounce);
//加载BMPANSRV.DLL
_LIT(KDllName, "BMPANSRV.DLL");
iBitmapAnimDll=RAnimDll(CCoeEnv::Static()->WsSession());
User::LeaveIfError(iBitmapAnimDll.Load(KDllName));
//创建动画播放对象
iBitmapAnim=new (ELeave) RBitmapAnim(iBitmapAnimDll);
RWindowBase* windowBase = iAppView->DrawableWindow();
iBitmapAnim->ConstructL(*windowBase);
//将动画容器传给播放对象
iBitmapAnim->SetBitmapAnimDataL(*iAnimFrames);
iBitmapAnim->SetNumberOfCyclesL(5);
iBitmapAnim->SetPositionL(TPoint(10,10));
//播放动画
iBitmapAnim->Start();
}