Наиболее простой вариант – использовать таймер (например - CPeriodic) для организации задержки между сменой кадров. По окончанию задержки рассчитывать и отображать очередной кадр. Реализация может быть следующей:
public: void DrawNewFrame();// отображение очередного кадра private: static void Period( TAny* aPtr); CPeriodic* iPeriodicTimer;
iPeriodicTimer= CPeriodic::NewL( CActive::EPriorityStandard); // указываем минимально возможные интервалы iPeriodicTimer->Start(1,1, TCallBack( CSmileDialog::Period, this));
void CYourAppView::Period( TAny* aPtr) { CYourAppView view= static_cast< CYourAppView*>( aPtr); // расcчет и отображение очередного кадра view->DrawNewFrame(); }
У данного подхода есть свои достоинства и недостатки.
Достоинства:
Недостатки:
ВSymbian представлены специальные возможности для создания качественнойанимации. Подход следующий:
Такой подход исключает недостатки, возникающие при реализациианимации на стороне клиента. Отрисовка кадров происходит в рамках высокоприоритетной серверной нити, количество одновременно функционирующих активных объектов, необходимых для отображения, легко сводится к минимуму. Однако у такого подхода, помимо возросшей сложности, есть еще один существенный недостаток. Для всех трех компонентов необходим расширенный набор возможностей (capabilities): PowerMgmt ReadDeviceData WriteDeviceData ProtServ. Соответственно, для установки этого примера на смартфон потребуется сертификат разработчика.
В качестве примера рассмотрим приложение в котором осуществляется вращение двух параллелепипедов в разных плоскостях.
Вращение осуществляется благодаря постоянному изменению углов поворота. Для расчета координат вершин параллелепипедов в каждый момент времени производится умножение вектора с координатами на матрицу поворота. Текущий параллелепипед выбирается с помощью кнопок 1,2. Приращение углов поворота можно менять(с помощью кнопок влево-вправо) и сбрасывать(кнопка 0) с помощью индикаторов - для этого нужный индикатор необходимо предварительно выбрать (вверх-вниз).
Исходный код данного примера:AnimExampleSrc.zip.
Для создания серверной DLL нужно реализовать собственного потомка класса CAnimDll, в нем должна быть реализована чистая виртуальная функция-фабрикаCAnim* CreateInstanceL( TInt aType ). Эта функция, как видно из прототипа, возвращает указатель на созданный объект-потомок абстрактного классаCAnim.
class CAnimServerDll:public CAnimDll { public: CAnimServerDll(); public: IMPORT_C CAnim* CreateInstanceL( TInt aType); };
ВSymbain представлены три потомка классаCAnim, которые можно использовать для создания анимации: CWindowAnim, CFreeTimerWindowAnim, CSpriteAnim. В этом примере используется класс, порожденный от CWindowAnim.
class CAnimation:public CWindowAnim,public MTimerNotifier { public: CAnimation( TInt aNo); virtual ~CAnimation(); public:// MTimerNotifier void TimedOut(); public: void ConstructL( TAny* aArgs, TBool aHasFocus); TInt CommandReplyL( TInt aCommand, TAny* aArgs); void Command( TInt aCommand, TAny* aArgs); void Redraw(); void Animate( TDateTime* aDateTime); void FocusChanged( TBool/*aState*/){} TBool OfferRawEvent(const TRawEvent&aRawEvent); private: CParallelepiped*iPrl;// вся логика расчета и отображения объекта TInt iNo;// номер объекта CAnimTimer* iServerTimer; };
Данный класс реализует интерфейс MTimerNotifier так как для задержки между отображением кадров используется собственный таймер CAnimTimer. Следует отметить, что у объектов, порожденных от CAnim, уже есть специализированный таймер, который может использоваться системой для аналогичных целей, однако в данном случае он не используется. Поэтому Timeout() содержит собственный вызов Animate() (с помощью члена-данного iFunctions, который предварительно отключает графический контекст, а затем вызывает метод Animate() данного класса) и инициирует новую задержку таймера.
void CAnimation::TimedOut() { // установка значения задержки таймера для следующего вызова iServerTimer->After( KTimerPeriod); // с помощью данного вызова происходит отрисовка текущего кадра iFunctions->Animate(0); }
Метод Animate(), в свою очередь, производит расчет новых координат вершин параллелепипеда и инициирует обновление экрана.
void CAnimation::Animate( TDateTime*/* aDateTime */) { // расчет новых координат iPrl->Calc(); // экран должен быть обновлен iWindowFunctions->Invalidate( iWindowFunctions->WindowSize()); }
Обновление экрана производится в методе Redraw() который отображает параллелепипед, координаты которого были рассчитаны предварительно.
void CAnimation::Redraw() { // отображение очередного кадра iPrl->Draw(*iGc); }
Методы CommandReply(), Command() - это обработчики команд, полученных от клиента. Оба метода объявлены как чистые виртуальные в предке, в качестве параметров, принимают идентификатор команды TInt aCommand и ее параметры TAny* aArgs. В данном примере используется только метод Command(). Рассмотрим, каким образом передаются параметры команд:
Клиентская часть инициирует команду:
TPckgBuf<tint> param; param()=55; RAnim::Command( KChangeGaugeValue, param);</tint>
Серверная часть ( CAnim :: Command() ) ее обрабатывает:
switch( aCommand) { ... case KChangeGaugeValue: TInt cmdValue=*( STATIC_CAST( TInt*, aArgs)); ... break; }
Для корректной сборки серверной библиотеки необходимо правильно задать ряд параметров проекта (см. mmp-файл):
> TARGETTYPE: ani UID2:0x10003b22
Для создания клиентской библиотеки необходимо реализовать собственного потомка класса RAnimDll
class RAnimClientDll:public RAnimDll { public: IMPORT_C RAnimClientDll( RWsSession& aSession); };
Кроме того, необходимо реализовать класс, который инициализирует серверную анимацию и предоставляет интерфейс для посылки команд. Класс должен быть потомком RAnim:
class RAnimation:public RAnim { public: IMPORT_C RAnimation( RAnimDll& aAnimDll); IMPORT_C void CreateAnimation( onst RWindowBase& aDevice, TInt aType, TDisplayMode aMode); IMPORT_C void AnimCommand( TInt aCommand,const TPtrC8&aArgs); IMPORT_C void AnimCommand( TInt aCommand); public: IMPORT_C void ChangeCurrentValue( TBool aInc); IMPORT_C void ChangeCurrent( TBool aNext); IMPORT_C TInt CurrentGauge(); // команды, обрабатываемые на сервере enum KAnimCommands { KReset=1, KResolutionChange=2, KChangeFocus=3, KChangeGauge=4, KChangeGaugeValue=5 }; private: TInt iDx, iDy, iDz;// значение приращений углов поворота TInt* iCurrent;// текущее приращение };
Метод CreateAnimation() используется для инициализации серверного объекта – для этого вызывается метод предка RAnim :: Construct(). В качестве параметра создаваемому объекту передается значение TDisplayMode, которое используется для конструирования объектов, отвечающих за двойную буферизацию.
Методы AnimCommand() используются для передачи команд серверному объекту, команды могут быть как с параметрами, так и без них.
Методы:
Для взаимодействия с серверной частью в рамках GUI, нужно использовать клиентскую библиотеку.
Для инициализации и загрузки сервером библиотеки с анимацией необходимо вызвать метод Load() у объекта класса RAnimClientDll (потомок RAnimDll). В качестве параметра этот метод принимает полное имя файла серверной библиотеки. Нужно учитывать - на эмуляторе и в реальном устройстве строки, содержащие полный путь до библиотеки, не совпадают.
Для каждого серверного объекта, отвечающего за отображение анимации, создается клиентская часть с помошью которой выполняется инициализация и посылка команд. В данном примере, для отображения двух параллелепипедов в рамках AppUi, создаются два объекта класса RAnimation. Эти объекта создаются в конструкторе AppUi, а в методе ConstructL() инициализируют серверную часть с помощью метода CreateAnimation().
AppUi содержит обработчик событий клавиатуры - метод HandleKeyEventL(). В нем, в зависимости от нажатой клавиши, вызываются функции объектов класса RAnimation результатом которых является посылка команд на сервер.
Замечание: для того чтобы приложение, содержащее данное GUI, успешно скомпоновалось, необходимо в список библиотек (в mmp-файле) добавить клиентскую библиотеку - в данном случае AnimClient.lib
Автор:Den.
Оригинал статьи размещен наForum Nokia Wiki.