Программируем смартфоны Symbian Series 60. Диалоги

Сегодня мы поговорим о диалоговых окнах. В S60 диалоговые окна бывают двух видов: диалог общего вида (generic dialog) и диалог запроса (query dialog). Для использования диалогов необходимо подключить к проекту библиотеку eikdlg.lib.

Диалог общего вида (generic dialog)


Диалог общего вида выгладит так же, как основное окно программы (см. рисунок).

Обычно диалог общего вида содержит только один управляющий элемент — список (или его разновидность CAknGrid). Он, конечно, может содержать и любой другой орган управления (редактор текста, слайдер и т.п.), но при этом будет выглядеть, мягко скажем, не очень хорошо. Разработчики S60 рекомендуют использовать список CAknSettingStyleListBox и на левую софт-клавишу вешать команду Options. По команде Options обычно вызывается диалог запроса, в котором и редактируется выбранный пункт списка. Давайте рассмотрим этот вариант (диалог будет иметь вид, показанный на рисунке). Диалоговое окно всегда загружается из ресурсов. Диалог в ресурсах описывается структурой DIALOG, которая содержит следующие поля: flags — флаги, определяющие работу диалога (объединяются с помощью операции ИЛИ);
buttons — имя CBA структуры, описывающей управляющие кнопки окна (о структуре CBA я рассказывал в одной их прошлых статей). Может использоваться как определенная пользователем структура, так и стандартная (например, R_AVKON_SOFTKEYS_OK_CANCEL, R_AVKON_SOFTKEYS_YES_NO и т.д.);
items — массив структур DLG_LINE. Используется только в одностраничных диалогах. Каждая структура DLG_LINE описывает элемент управления, включаемый в диалог;
pages — имя массива структур PAGE. Используется только в многостраничных диалогах. Каждая структура PAGE описывает одну страницу диалога; form — имя структуры FORM, описывающей страницу в одностраничном диалоге. Этот механизм альтернативен использованию поля items и появился начиная с S60.2fp2.

Поля items, pages и form взаимоисключающие, т.е. вы можете использовать только одно из них (остальные не определяются). Если вы все же хотите создать диалог, содержащий не только список, то лучше не использовать items, а пользоваться полем forms (диалог будет лучше выглядеть). В структуре DIALOG есть еще поле title (заголовок окна), однако в S60 оно не используется, т.к. диалоги в S60 не имеют заголовков.

Заголовок, который вы видите на рисунке, это заголовок не диалога, а основного окна приложения.

При описании диалога обычно используются следующие флаги:
EEikDialogFlagWait — функция CEikDialog::ExecuteLD() не вернет управления, пока диалог не закроется;
EEikDialogFlagFillScreen или EEikDialogFlagFillAppClientRect — взаимоисключающие флаги и определяются только для диалогов общего. Если определен флаг EEikDialogFlagFillAppClientRect, то диалог будет занимать всю центральную панель, а если EEikDialogFlagFillScreen — то весь экран;
EEikDialogFlagNoTitleBar — используется по умолчанию, однако его обычно все равно указывают на тот случай, если в будущих версиях S60 он не будет задаваться по умолчанию;
EEikDialogFlagCbaButtons — не используется, однако указывается для совместимости.

Другие флаги вы можете посмотреть в справке SDK.
Для диалогов запроса определяется только один флаг EGeneralQueryFlags.
Структура DLG_LINE описывает строку диалога и содержит следующие поля:

type — константа, определяющая тип управляющего элемента. Все константы для стандартных управляющих элементов собраны в заголовочных файлах eikon.hrh и avkon.hrh. Часто для того, чтобы узнать, какая константа соответствует определенному классу, достаточно поступить следующим образом: для классов с именем, начинающимся с CAkn, надо взять имя класса и заменить CAkn на EAknCt (например, для класса CAknSlider константа EAknCtSlider), для списков еще надо выкинуть из названия Style (например, для класса СAknSingleStyleListBox константа EAknCtSingleListBox), для классов с именем, начинающимся с CEik, надо заменить в имени CEik на EEikCt (например, для класса СEikEdwin константа EEikCtEdwin); prompt — строка текста, располагающаяся слева от управляющего элемента (для списков лучше не задавать);
id — определяемая программистом в hrh-файле константа-идентификатор управляющего элемента;
itemflags — набор флагов для управляющего элемента;
control — структура, описывающая дополнительные параметры управляющего элемента. Для каждого типа элемента она своя. Например, для
CEikEdwin это EDWIN, для CAknSlider — SLIDER и т.д (см. предыдущую статью);
trailer — текст, помещаемый после управляющего элемента.
Начиная с S60.2fp2 появились еще поля:
bmpfile — имя mbm-файла с оконкой;
bmpid — номер изображения иконки в mbm-файле;
bmpmask — номер маски иконки в mbm-файле;
tooltip — справочный текст для управляющего элемента.

Из всех этих полей обязательными являются только type и id. Пример описания диалога общего вида в ресурсах:

RESOURCE DIALOG r_my_settings_dialog {
flags = EEikDialogFlagNoDrag | EEikDialogFlagFillAppClientRect | EEikDialogFlagCbaButtons | EEikDialogFlagWait;
buttons = R_AVKON_SOFTKEYS_OK_CANCEL;
items = {
DLG_LINE {
type = EAknCtSettingListBox;
id = EMySettingsList;
control = LISTBOX {
flags = EAknListBoxSelectionList;
};
}
};
}

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

SEikControlInfo CStatisticsDialog::CreateCustomControlL( TInt aControlType) — эта функция используется для создания пользовательских элементов управления;
void PreLayoutDynInitL() — эта функция вызывается системой после того как класс диалогового окна создан, загружены ресурсы и созданы все элементы управления, но еще не определены их размеры. Здесь обычно элементам управления задаются начальные значения;
void PostLayoutDynInitL() — вызывается после PreLayoutDynInitL() и после того, как заданы размеры и положение управляющих элементов. Эта функция также используется для инициализации управляющих элементов;
TBool OkToExitL(TInt aButtonId) — вызывается, когда вы нажимаете софт-клавишу (код нажатой клавиши передается как аргумент). Если функция возвращает значение ETrue, то диалоговое окно закрывается, а если EFalse — не закрывается. Так как при закрытии окна также уничтожается класс CEikDialog, то в этой функции тоже обычно сохраняются отредактированные значения из управляющих элементов диалога.

Вот так будет выглядеть класс диалога для нашего ресурса:

class CMySettingsDialog : public CEikDialog {
private: TBuf<100> &iName, &iAddress;
TBuf<25> &iPhone;
public: CExampleGenericDialog(TBuf<100> &aName, TBuf<100> &aAddress, TBuf<25> &aPhone) : iName(aName), iAddress(aAddress), iPhone(aPhone) {;}
protected: void PreLayoutDynInitL();
TBool OkToExitL(TInt aButtonId);
};
void CMySettingsDialog::PreLayoutDynInitL() {
CEikTextListBox* list = (CEikTextListBox*) Control(EMySettingsList);
if(list) {
MDesCArray* textArray = list->Model()->ItemTextArray();
CDesCArray* itemList = static_cast<CDesCArray*>(textArray);
TBuf<120> buf;
buf = _L("\tName\t\t");
buf.Append(iName);
itemList->AppendL(buf);
buf = _L("\tAddress\t\t");
buf.Append(iAddress);
itemList->AppendL(buf);
buf = _L("\tPhone\t\t");
buf.Append(iPhone);
itemList->AppendL(buf);
list->HandleItemAdditionL();
list->CreateScrollBarFrameL(ETrue);
list->ScrollBarFrame()->SetScrollBarVisibilityL(CEikScrollBarFrame::EOn, CEikScrollBarFrame::EAuto);
}
}
TBool CMySettingsDialog::OkToExitL(TInt aButtonId) {
if(aButtonId == EAknSoftkeyOptions) {
... // Вызываем редактор для пункта
return EFalse;
}
return ETrue;
}

Как вы видите из примера, доступ к управляющим элементам диалога осуществляется по идентификатору EMySettingsList с помощью функции Control(). EMySettingsList определяем в hrh-файле, просто добавив его к уже перечисленным там идентификаторам. Описанное диалоговое окно вызывается следующим образом:

TBuf<100> iName, iAddress;
TBuf<25> iPhone;
iName = _L("Ivanov");
iAddress = _L("Minsk, XXX str., 1-123");
iPhone = _L("+37529XXXXXXX");
CMySettingsDialog dialog = new (ELeave) CMySettingsDialog(name, iAddress, iPhone);
dialog->ExecuteLD(R_MY_SETTINGS_DIALOG);
Функция ExecuteLD() в конце имени имеет букву D, а это значит, что перед тем, как вернуть управление, она удаляет переменную dialog, т.е. вам не надо вызывать для нее оператор delete.

Диалог запроса (query dialog)

Внешний вид диалог запроса отличается от диалога общего вида (см. рисунок). Важным отличием диалогов запроса является то, что он может содержать только один или два управляющих элемента.

Доступны следующие классы диалогов запроса:
CAknTextQueryDialog — диалог с редактором текста (показан на рисунке);
CAknNumberQueryDialog — диалог для ввода целых чисел;
CAknTimeQueryDialog — диалог для ввода даты или времени;
CAknDurationQueryDialog — диалог для ввода временного интервала;
CAknFixedPointQueryDialog — диалог для ввода чисел с фиксированной запятой;
CAknFloatingPointQueryDialog — диалог для ввода чисел с плавающей запятой;
CAknIpAddressQueryDialog — диалог для ввода IP-адреса;
CAknListQueryDialog — диалог со списком для выбора одного пункта или нескольких (во втором случае к каждому пункту добавляется чек-бокс);
CAknMultiLineDataQueryDialog — диалог, содержащий два органа управления: два редактора даты/времени, или два текстовых редактора, или редактор текста и даты/времени, или редактор текста и редактор целых чисел, или редактор текста и временного интервала, или редактор даты/времени и временного интервала, или два редактора целых чисел.

Диалог запроса так же, как и диалог общего вида, должен загружаться из ресурсов приложения. В ресурсах диалог запроса описывается структурой DIALOG. Полю flag присваивается значение EGeneralQueryFlags. В поле items задается одна или две структуры DLG_LINE.

В структуре DLG_LINE поле type задается как EAknCtQuery для всех типов управляющих элементов, поле id всегда задается как EGeneralQuery. В поле control задается структура, описывающая параметры управляющего элемента: для класса CAknListQueryDialog это структура
AVKON_LIST_QUERY_CONTROL, для остальных классов — AVKON_DATA_QUERY.

В структуре AVKON_DATA_QUERY необходимо задать три поля:
label — строка текста над управляющим элементом;
layout — тип управляющего элемента;
control — структура, описывающая управляющий элемент (см. предыдущую статью).

Возможны следующие значения поля layout:

EDataLayout — простой редактор (в поле control задается структура EDWIN, в качестве класса диалога используется CAknTextQueryDialog); ECodeLayout — редактор для ввода пароля, символы отображаются звездочками (control = SECRETED, класса — CAknTextQueryDialog);
EPhoneLayout — редактор для ввода строк, состоящих из чисел (control = EDWIN, в качестве класса диалога используется CAknTextQueryDialog); EPinLayout — редактор для ввода пароля, состоящего из чисел (PIN-код), символы отображаются звездочками (control = SECRETED, класс — CAknTextQueryDialog);
ENumberLayout — редактор для ввода целых чисел (control = AVKON_INTEGER_EDWIN, класс — CAknNumberQueryDialog);
ETimeLayout — редактор для ввода времени (control = TIME_EDITOR, класс — CAknTimeQueryDialog);
EDateLayout — редактор для ввода даты (control = DATE_EDITOR, класс — CAknTimeQueryDialog);
EDurationLayout — редактор для ввода временного интервала (control = DURATION_EDITOR, класс — CAknDurationQueryDialog);
EFloatingPointLayout — редактор для ввода чисел с плавающей запятой (control = FLPTED, класс — CAknFloatingPointQueryDialog);
EFixedPointLayout — редактор для ввода чисел с фиксированной запятой (control = FIXPTED, класс — CAknFixedPointQueryDialog);
EIpLayout — редактор для ввода IP-адресов (control = IP_FIELD_EDITOR, класс — CAknIpAddressQueryDialog).

Рассмотрим пример диалога для ввода строки текста. Сначала описание ресурса:

RESOURCE DIALOG r_name_edit_query {
flags = EGeneralQueryFlags;
buttons = R_AVKON_SOFTKEYS_OK_CANCEL;
items = {
DLG_LINE {
type = EAknCtQuery;
id = EGeneralQuery;
control = AVKON_DATA_QUERY {
layout = EDataLayout;
label = "Name";
control = EDWIN {
width = 5;
lines = 1;
maxlength = 100;
};
};
}
};
}

Теперь код для вызова диалога:

TBuf<100> text;
CAknTextQueryDialog* dialog = CAknTextQueryDialog::NewL(text);
dialog->PrepareLC(R_NAME_EDIT_QUERY);
if(dlg->RunLD() == EAknSoftkeyOk) {
...
}

В предыдущем примере для вызова диалога мы использовали функцию ExecuteLD, которая сначала вызывает PrepareLC (загрузка диалога из
ресурсов), а затем RunLD. Эти два способа равноценны, однако при использовании связки PrepareLC-RunLD вы можете после вызова PrepareLC производить дополнительные инициализации (например, передвинуть окно, поменять заголовок, добавить пункты в список). Тем самым вы можете выполнить все нужные действия по инициализации без порождения нового класса диалога. При создании диалога запроса в виде списка в структуре AVKON_LIST_QUERY_CONTROL необходимо задать поля:

heading — необязательный заголовок списка;
listtype — тип списка. Возможны варианты: EAknCtSinglePopupMenuListBox, EAknCtSingleGraphicPopupMenuListBox,
EAknCtSingleGraphicHeadingPopupMenuListBox, EAknCtMenuDoublePopupMenuListBox, EAknCtSinglePopupSubmenuListBox,
EAknCtDoubleLargeGraphicPopupMenuListBox, EAknCtSingleHeadingPopupMenuListBox. Каждый тип соответствует определенному классу. По константе довольно просто определить, какому классу она соответствует (см. предыдущую статью), поэтому расписывать это я не буду;
listbox — структура LISTBOX, описывающая параметры списка (см. предыдущую статью).

Приведем пример диалога запроса в виде списка. Сначала описание ресурса:
RESOURCE DIALOG r_my_list_query {
flags = EGeneralQueryFlags;
buttons = R_AVKON_SOFTKEYS_OK_CANCEL;
items = {
DLG_LINE {
type = EAknCtListQueryControl;
id = EListQueryControl;
control = AVKON_LIST_QUERY_CONTROL {
listtype = EAknCtSinglePopupMenuListBox;
listbox = LISTBOX {
flags = EAknListBoxMenuList;
height = 3;
width = 10;
array_id = r_my_list_query_item;
};
};
}
};
}
RESOURCE ARRAY r_my_list_query_item {
items = {
LBUF {txt = "First item"; },
LBUF {txt = "Second item"; },
LBUF {txt = "Third item"; }
};
}
Теперь код для вызова диалога:
TInt index = 0;
CAknListQueryDialog* dlg = new( ELeave ) CAknListQueryDialog( &index );
if(dlg->ExecuteLD(R_MY_LIST_QUERY) == EAknSoftkeyOk) {
...
}

Результат показан на рисунке.

Наиболее часто используемыми диалогами являются диалоги выдачи сообщений. Для выдачи сообщений обычно используют функцию InfoWinL() класса CEikonEnv. Например:
CEikonEnv::Static()->InfoWinL(_L("Message"), _L(""));

В результате появится диалог запроса со строкой сообщения и кнопкой Back. В некоторых Symbian-системах — например, UIQ — первая строка сообщения заносится в заголовок окна, а вторая выводится в окне. Так как у диалогов в S60 нет заголовка, то InfoWinL() выводит обе строки сообщения в окне.

Если необходимо задать вопрос пользователю, то используется функция QueryWinL класса CEikonEnv, которая отображает диалог с кнопками yes и no. Функция возвращает значение ETrue, если пользователь нажал yes, и EFalse — если no. Например:

if(CEikonEnv::Static()->QueryWinL(_L("Yes or no?"), _L(""))) {...}

Функции InfoWinL и QueryWinL выдают просто текстовое сообщение. Если же вы хотите, чтобы у вашего сообщения была еще и иконка, то вам необходимо определить в ресурсах диалог запроса с полем control = AVKON_CONFIRMATION_QUERY. Структура AVKON_CONFIRMATION_QUERY имеет следующие поля:
layout = EConfirmationQueryLayout;
label — текст сообщения;
bmpfile — имя mbm-файла, содержащего иконку;
bmpid — номер изображения иконки в mbm-файле;
bmpmask — номер маски иконки в mbm-файле.
Для создания диалога должен использоваться класс CAknQueryDialog.

На этом на сегодня все.

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


Компьютерная газета. Статья была опубликована в номере 06 за 2007 год в рубрике mobile

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