...
...

Программируем смартфоны Symbian Series 60. Оформление основного окна приложения

Сегодня мы с вами разберем, какие элементы управления есть у окна приложения, и как ими управлять.

Области окна приложения

У окна приложения выделяют три области (см. рисунок):
1. Панель статуса.
2. Главная панель.
3. Управляющая панель.

Управляющая панель

В управляющей панели отображаются команды, связанные с двумя программируемыми клавишами (еще называемыми софт-клавишами). Команды для управляющей панели описываются в ресурсах приложения с помощью структуры CBA. Вы должны связать массив из одной или двух структур (так как софт- клавиш всего две) типа CBA_BUTTON с полем buttons структуры CBA. Например:

RESOURCE CBA r_my_cba {
buttons = {
CBA_BUTTON {
id = EMySoftkeyCommand;
txt = "Select";},
CBA_BUTTON {
id = EAknSoftkeyBack;
txt = "Back";}
};
}

В этом примере описана управляющая панель, в которой с левой софт-клавишей связывается команда EMySoftkeyCommand и метка Select, а с правой — команда EAknSoftkeyBack и метка Back. При нажатии софт-клавиши соответствующий код будет посылаться функции HandleCommandL вашего класса, порожденного от CAknAppUi.

EAknSoftkeyBack — это системная команда, объявленная в файле avkon.hrh. EMySoftkeyCommand — пользовательская команда. Пользовательские команды, а также другие идентификаторы ресурсов описываются как элементы перечисления и объявляются обычно в файле с расширением .hrh. Пример — файл resource.rhr:
#ifndef __RESOURCE_HRH__
#define __RESOURCE_HRH__
enum TMyCommandsIds {
EMySoftkeyCommand = 0x1001,
EMySoftkeyCommand2
};
#endif

Этот файл необходимо подключить к файлу ресурсов с помощью директивы include. Внимание: не должно быть команд или идентификаторов с кодом 0. Каждая программа имеет панель управления по умолчанию, которая определяется полем cba структуры EIK_APP_INFO в ресурсах приложения (о данной структуре мы уже упоминали в первой статье цикла). Для нашего примера:
RESOURCE EIK_APP_INFO {
cba = r_my_cba;
}

Для работы с панелью управления используется класс CEikButtonGroupContainer, доступ к которому осуществляется через функцию Cba() класса CAknAppUi. Класс CEikButtonGroupContainer позволяет как целиком менять панель управления, так и управлять отдельными кнопками. Для смены панели управления используется функция SetCommandSetL(). Например:
Cba()->SetCommandSetL(R_MY_CBA);

Заметьте: когда мы описывали структуру CBA в ресурсах, мы писали ее имя r_my_cba маленькими буквами, а когда используем ее в программе, пишем имя уже заглавными буквами. Ошибки здесь нет. Дело в том, что, когда ресурсы компилируются, то имена всех ресурсов приводятся к верхнему регистру и помещаются в файл, имя которого совпадает с именем файла ресурсов и имеет расширение .rsg. .rsg-файл помещается в системный каталог epoc32\include, поэтому он должен подключаться следующим образом:
#include <helloworld.rsg>

Если вы посмотрите наш пример HelloWord, то там вы не найдете в ресурсах структуры CBA, а в качестве стандартной панели управления используется R_AVKON_SOFTKEYS_OPTIONS_EXIT. Дело в том, что разработчики ОС заранее описали наиболее часто используемые структуры CBA, одной из которых и является R_AVKON_SOFTKEYS_OPTIONS_EXIT. Имена всех стандартных CBA-структур начинаются с R_AVKON_SOFTKEYS_,
а далее в имени указаны имена команд софт-клавиш. Коды соответствующих команд будут начинаться с EAknSoftkey и заканчиваться именем команды. 

Например, для R_AVKON_SOFTKEYS_OPTIONS_EXIT это EAknSoftkeyOptions и EAknSoftkeyExit. Полный список стандартных структур CBA вы можете посмотреть в файле avkon.rsg в системном каталоге epoc32\include. Особую роль в управляющей панели играет кнопка, с которой связывается команда EAknSoftkeyOptions. По этой команде вызывается меню приложения. Данную команду рекомендуется связывать с левой софт-клавишей. Меню приложения описывается в ресурсах приложения с помощью структуры MENU_BAR, которая содержит только одно поле titles, в котором указывается главная панель меню (в Series60 главная панель должна быть одна). Панель меню может содержать как команды, так и подменю. Содержимое панели меню определяется как массив структур MENU_ITEM. Каждая структура MENU_ITEM содержит имя пункта меню (поле txt), команду, соответствующую пункту (поле command), и имя панели подменю (необязательное поле cascade). Остальные поля MENU_ITEM в Series60 не используются. Пример:

RESOURCE MENU_BAR r_my_menubar {
titles = { MENU_TITLE { menu_pane = r_my_menu;}};
}
RESOURCE MENU_PANE r_my_menu {
items = {
MENU_ITEM {
command = EMyMenuCommand1;
txt = "My command 1";
},
MENU_ITEM {
command = EMySubmenuCommand;
cascade = r_my_submenu;
txt = "Submenu";
}
};
}
RESOURCE MENU_PANE r_my_submenu {
items = {
MENU_ITEM {
command = EMyMenuCommand2;
txt = "My command 2";
}
};
}

Меню приложения задается полем menubar структуры EIK_APP_INFO. Например:
RESOURCE EIK_APP_INFO {
cba = R_AVKON_SOFTKEYS_OPTIONS_EXIT;
manubar = r_my_menubar;
}

В отличие от управляющей панели, поменять меню в ходе работы приложения нельзя (правда, в многостраничных приложениях можно каждой странице назначить свое меню, но эту тему мы сегодня рассматривать не будем). Однако вы можете управлять панелями меню: удалять, добавлять, скрывать или запрещать различные пункты меню. Давайте рассмотрим, как это делать. Для начала разберемся, как работает меню. Когда вы нажимаете софт-клавишу, с которой связана команда EAknSoftkeyOptions, из ресурсов создается объект CEikMenuPane, и вызывается функция DynInitMenuPaneL(TInt aResourceId, CEikMenuPane *aMenuPane) вашего класса, порожденного от CAknAppUi. У функции DynInitMenuPaneL два аргумента: первый — идентификатор ресурса панели меню, второй — указатель на созданный объект CEikMenuPane. 
В функции DynInitMenuPaneL вы можете выполнять любые действия с пунктами панели меню.

Например:
void CHelloWordAppUi::DynInitMenuPaneL(TInt aResourceId, CEikMenuPane *aMenuPane) {
switch(aResourceId) {
case R_MY_MENU:
{ CEikMenuPaneItem::SData item;
item.iCommandId = EAknSoftkeySelect;
item.iText = _L("Select");
aMenuPane->AddMenuItemL(item);
break;
}
case R_MY_SUBMENU:
aMenuPane->SetItemDimmed(EMyMenuCommand2, ETrue);
break;
}
}

В этом примере мы добавляем в панель R_MY_MENU меню еще один пункт, а в панели подменю R_MY_SUBMENU запрещаем команду EMyMenuCommand2. Только после того, как функция DynInitMenuPaneL вернула управление, панель меню отображается на экране. После закрытия меню связанный с ним объект CEikMenuPane уничтожается. Поэтому нельзя сохранять указатель на объект CEikMenuPane, получаемый функцией DynInitMenuPaneL, и использовать его еще где-либо, кроме этой функции.

Панель статуса

Панель статуса делится на несколько областей (см. рисунок):
1. Панель уровня сигнала.
2. Контекстная панель.
3. Панель заголовка окна.
4. Панель уровня заряда батареи.
5. Навигационная панель.

Панель статуса описывается классом CEikStatusPane, доступ к которому осуществляется через функцию StatusPane() класса CAknAppUi. Панелями уровня сигнала и уровня заряда батареи управляет операционная система, и ваша программа не должна вмешиваться в их отображение. В контекстной панели отображается иконка приложения. В приложении HelloWorld у нас была стандартная иконка. Давайте разберемся, как сделать свою. Первое, что мы должны сделать, — это нарисовать две картинки в bmp-формате размером 44х44 и 42х29 и две соответствующие им черно-белые маски (черный цвет обозначает, что в данной точке рисуется точка фонового рисунка, а белый цвет — точка иконки). После этого вы должны создать файл ресурсов иконки. Пример — файл HelloWorldAif.rss:

#include <aiftool.rh>
RESOURCE AIF_DATA {
app_uid=0x10005B91;
num_icons=2;
}

где в поле app_uid указывается UID вашего приложения, а в поле num_icons — количество иконок (обычно 2: 44х44 и 42х29). Далее в файл проекта (файл с расширение .mmp) добавляем описание иконки. Например:

AIF HelloWorld.aif . HelloWorldAif.rss c16 context_pane_icon.bmp 
context_pane_icon_mask.bmp list_icon.bmp list_icon_mask.bmp

где AIF — ключевое слово, обозначающее тип ресурса, HelloWorld.aif — имя иконки (оно должно совпадать с именем программы), третий элемент — каталог с файлом ресурсов иконки (точка — текущий каталог), с16 — количество цветов в создаваемой иконке (с обозначает color, 16 — это 16 бит на точку, т.е. 65536 цветов; возможны черно-белые варианты: 1, 2, 4, 8 и цветные варианты: с4, с8, с16 и с24), далее перечислены исходные изображения для иконки с масками. 
После компиляции программы иконка будет находиться в каталоге 
\SYMBIAN\7.0s\Series60_v20\Epoc32\data\z\system\apps\<имя вашей программы>\ 
Не забудьте включить иконку в ваш инсталляционный файл. Например, для приложения HelloWorld в pkg-файл надо добавить строку:
\SYMBIAN\7.0s\Series60_v20\data\z\system\apps\HelloWorld\HelloWorld.aif" — "!:\system\apps\HelloWorld\HelloWorld.aif"

Если вы хотите, чтобы иконка отображалась на эмуляторе, скопируйте aif-файл в каталог \SYMBIAN\7.0s\Series60_v20\Epoc32\release\wins\udeb\z\system\apps\<имя вашей программы>\. Теперь у вас в контекстной панели должна появиться ваша иконка (правда, эмулятор все же иногда отказывается ее показывать, но на устройстве все должно быть в порядке). Для управления содержимым контекстной панели используется класс CAknContextPane. Доступ к этому классу осуществляется из класса CAknAppUi следующим образом:

CAknContextPane *context_pane = (CAknContextPane*) 
StatusPane()->ControlL( Uid::Uid(EEikStatusPaneUidContext));

Перейдем теперь к панели заголовка. В панели заголовка отображается название программы. Для приложения HelloWorld мы не определяли название программы и ярлыка, поэтому в качестве названия отображалось имя app-файла. Для того, чтобы добавить имя ярлыка и заголовок основного окна приложения, необходимо создать файл ресурсов с именем <имя приложения>_caption.rss. Например, для приложения HelloWorld этот файл должен иметь имя HelloWorld_caption.rss и содержать следующее:

#include <apcaptionfile.rh>
RESOURCE CAPTION_DATA {
caption = "Hello World";
shortcaption = "Hello World";
}

Имя, определяемое полем caption, будет отображаться в заголовке окна, а shortcaption — в Application Manager. 

Caption-файл также надо включить в pkg-файл: "\SYMBIAN\7.0s\Series60_v20\epoc32\data\z\system\apps\HelloWorld\HelloWorld_caption.rsc" — "!:\system\apps\HelloWorld\HelloWorld_caption.rsc"
Для управления содержимым панели заголовка используется класс CAknTitlePane. Доступ к этому классу осуществляется из класса CAknAppUi следующим образом:
CAknTitlePane *title_pane = (CAknTitlePane*) StatusPane()->ControlL( Uid::Uid(EEikStatusPaneUidTitle));

Разработчики Series60 не рекомендуют изменять содержимое контекстной панели и панели заголовка в ходе выполнения приложения. Для отображения дополнительной информации рекомендуется использовать навигационную панель. 
Навигационная панель описывается классом CAknNavigationControlContainer и представляет собой контейнер, в который может помещаться различная информация: некоторый текст, изображения, закладки или индикаторы. Получить доступ к навигационной панели можно следующим образом:
CAknNavigationControlContainer *navigation = (CAknNavigationControlContainer*) StatusPane()->ControlL(TUid::Uid(EEikStatusPaneUidNavi));

Для того чтобы добавить что-либо в навигационную панель, надо выполнить следующие действия:
1. Создать содержимое с помощью одной из функций Create класса CAknNavigationControlContainer;
2. Добавить созданный объект в панель с помощью функции PushL();
3. Перерисовать панель с помощью функции DrawNow().

Пример — добавление в навигационную панель строки текста:
CAknNavigationDecorator *decarator = navigation->CreateNavigationLabelL("Text");
navigation->PushL(*decarator);
navigation->DrawNow();

Удаление информации из навигационной панели осуществляется следующим образом:
navigation->Pop();
delete decarator;

С навигационной панелью на сегодня все, но в будущем мы к ней еще вернемся, когда будем рассматривать многостраничные приложения с
закладками.

Особенности разработки приложений для Series60 3rd Edition.

Все новые смартфоны, выпускаемые корпорацией Nokia, базируются на третьей редакции платформы Series60 (далее S60.3). При разработке приложений для S60.3 есть некоторые отличия от программы для первой и второй редакции. Рассмотрим сначала изменения в исходном коде программ. Не считая новых API их всего два:

1. Изменилась точка входа в программу. В S60.1 и S60.2 она выглядела так:
GLDEF_C TInt E32Dll(TDllReason /*aReason*/) {
return KErrNone;
}
а в S60.3 — так:
GLDEF_C TInt E32Main() {
return EikStart::RunApplication( NewApplication );
}

2. Изменился базовый конструктор класса CAknAppUi. Ранее было так:
void CHelloWorldAppUi::ConstructL() {
BaseConstructL();
...
а в S60.3 — так:
void CHelloWorldAppUi::ConstructL() {
BaseConstructL(EAknEnableSkin);
...

Теперь изменения в ресурсах. Они более значительны:

1. Исполняемые файлы в S60.3 имеют расширение .exe, а не .app, как в S60.1 и S60.2. В связи с этим в файле проекта необходимо указывать тип файла EXE. Например:
TARGET HelloWorld.exe
TARGETTYPE exe
Также в файл проекта надо добавить следующее:
SECUREID 0x200042D6
VENDORID 0
CAPABILITY None // или список CAPABILITY

2. Файл иконки имеет расширение .mif (а не .aif как, ранее) и создается на основе изображения в векторном svg-формате. 
Для описания иконки используется специальный mk-файл. Пример — файл HelloWorld.mk:
ifeq (WINSCW,$(findstring WINSCW, $(PLATFORM)))
ZDIR=$(EPOCROOT)epoc32\release\$(PLATFORM)\$(CFG)\Z
else
ZDIR=$(EPOCROOT)epoc32\data\z
endif
TARGETDIR=$(ZDIR)\resource\apps
ICONTARGETFILENAME=$(TARGETDIR)\HelloWorld_aif.mif
ICONDIR=.
do_nothing :
@rem do_nothing
MAKMAKE : do_nothing
BLD : do_nothing
CLEAN : do_nothing
LIB : do_nothing
CLEANLIB : do_nothing
RESOURCE :
mifconv $(ICONTARGETFILENAME) /c32 $(ICONDIR)\HelloWorld.svg
FREEZE : do_nothing
SAVESPACE : do_nothing
RELEASABLES :
@echo $(ICONTARGETFILENAME)
FINAL : do_nothing
Для компиляции иконки в секцию PRJ_MMPFILES файла bld.inf надо добавить строку:
gnumakefile HelloWorld.mk

3. Больше не используется файл заголовка _caption.rss. Вместо него должен создаваться специальный файл регистрации приложения. 
Вот пример такого файла (HelloWorld_reg.rss):
#include <appinfo.rh>
#include < HelloWorld.rsg>
UID2 KUidAppRegistrationResourceFile
UID3 0xA000017F // UID вашего приложения
RESOURCE APP_REGISTRATION_INFO {
app_file="HelloWorld"; // Имя exe-файла без расширения
localisable_resource_file = "\\resource\\apps\\ HelloWorld";
localisable_resource_id = R_HELLOWORLD_LOCALISABLE_APP_INFO;
embeddability = KAppNotEmbeddable;
newfile = KAppDoesNotSupportNewFile;
}

Для поддержки файла регистрации в файл ресурсов должен быть добавлен ресурс LOCALISABLE_APP_INFO. Пример:
RESOURCE LOCALISABLE_APP_INFO r_helloworld_localisable_app_info {
short_caption = "Hello World";
caption_and_icon = CAPTION_AND_ICON_INFO {
caption = "Hello World";
number_of_icons = 1;
icon_file = "\\resource\\apps\\HelloWorld_aif.mif";
};
}

4. Изменились каталоги приложения на устройстве. В S60.1 и S60.2 все файлы приложения располагались в каталоге !:\system\apps\<имя приложения>\. В S60.3 ресурсы должны располагаться так:
!:\sys\bin\ — exe-файл;
!:\resource\apps\ — файл ресурсов и иконка приложения;
!:\private\10003a3f\import\apps\ — файл регистрации приложения;
!:\private\<UID вашего приложения>\ — все остальные файлы.
Первые три каталога ваше приложение может только читать, к последнему разрешен полный доступ. Все остальные каталоги смартфона для вас закрыты. Часть из них можно открыть, но для этого вы должны получить цифровую подпись к вашей программе в компании Symbian, доказав, что вашему приложению это действительно нужно.

5. Часть API по умолчанию закрыта (например, работа с сетью, персональными данными, полный доступ к файловой системе и т.д.). Для получения доступа к ним в файле проекта в секции CAPABILITY требуется указать, какие возможности вам необходимы и при выпуске финальной версии программы получить в компании Symbian цифровую подпись, разрешающую данные API. В справочной системе SDK для каждой функции помечено, какие CAPABILITY ей нужны. Сразу отмечу, что все API, относящиеся к пользовательскому интерфейсу и графике, никаких CAPABILITY не требуют. И в конце об особенностях компиляции проектов для S60.3. Конечно же, вам нужен SDK 3 или SDK 3 FP1. Для использования Visual Studio 2003 еще необходим пакет Carbide.vs (его можно скачать с forum.nokia.com). Для компиляции проекта для устройства надо использовать команду
abld build gcce urel

Для создания проекта для Visual Studio 2003 используется команда
makmake HelloWorld.mmp vs2003
В остальном процесс идентичен компиляции проекта для S60.1 и S60.2.

На сегодня все. В следующий раз мы займемся главной панелью приложения.

Алексей Аношенко

© Компьютерная газета

полезные ссылки
Корпусные камеры видеонаблюдения
IP камеры видеонаблюдения