Знакомство с FireUI

Недавно мы опубликовали серию статей, посвящённых разработке приложений в FireMonkey. Тогда мы описали ключевые моменты построения приложения, в том числе создание базы данных, подключение данных с помощью технологии LiveBinding, развёртывание приложения на мобильной платформе. Однако, детально рассматривать ньюансы создания мобильных приложений мы не стали. Во многом это связано с тем, что сам процесс мобильной разработки в Delphi эволюционирует от версии к версии. В частности, в последней XE7 на сегодняшний день версии Delphi, был представлен новый дизайнер форм FireUI Multi-Device Designer. В данной статье с помощью небольшого примера мы рассмотрим, что же из себя представляет FireUI и каким образом с его появлением изменилась методология разработки.0b4cbc534fdf4129bcd1f0f11d157260.jpgДля этого мы создадим простейшее приложение для тестирования. Пользователю на экран выводится картинка, содержащая вопрос и несколько вариантов ответов. Мы выбрали футбольную тематику (почему бы и нет?).

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

Наиболее естественным выбором СУБД для мобильных приложений является SQLite. В качестве менеджера БД можно использовать бесплатную версию SQLite Expert или любой другой продукт для работы с SQLite.

База будет содержать единственную таблицу, в которой будет храниться информация о футболитах. SQL-код, с помощью которого создаётся таблица, будет выглядеть примерно так:

CREATE TABLE [Player] ( [Id] INTEGER NOT NULL ON CONFLICT ROLLBACK PRIMARY KEY ON CONFLICT ROLLBACK AUTOINCREMENT, [PlayerName] CHAR (100) NOT NULL, [TeamNumber] INTEGER, [Height] INTEGER, [Weight] INTEGER, [Photo] BLOB, [CountryId] INTEGER); Собственно, в данной статье мы используем только поля Id, PlayerName и Photo. Остальное — задел на будущее. Заполнить таблицу тестовыми данными мы также сможем с помощью SQLite Expert.По окончании подготовки базы приступим непосредственно к написанию программы.

В XE7 был несколько изменён подход к созданию FireMonkey-приложений. Теперь между «настольным» и мобильным приложением нет принципиальной разницы. Как следствие, при создании приложения мы выбираем шаблон Multi-Device Application.

5d6d3cb935034939afc47f3c6887da92.jpg

Такой проект способен генерировать исполняемые файлы для любой из платформ, поддерживаемых Delphi: Windows, OS X, IOS и Android. Мы сможем выбрать конкретную платформу в ходе создания проекта, а также настроить пользовательский интерфейс для конкретного типа устройств.

Как и в предыдущих версиях мы можем выбрать шаблон главной формы проекта с помощью диалога Select a Multi-Device Application Type.

9500626cbb3e486f89952d4b8b68001d.jpg

Для нашего приложения подойдёт шаблон Tabbed.

Прежде всего настроим подключение к БД с помощью FireDAC. Для этого последовательно поместим на форму три компонента — TFDConnection, TFDPhysSQLiteDriverLink и TFDGUIxWaitCursor. Двойной щелчок по компоненту TFDConnection вызывает редактор соединения. Здесь нам следует указать путь к базе данных. Свойству LoginPrompt присвоим значение False.

6c94e116ed8a4e71b72af24bae598a2b.jpg

Для формирования списка вопросов и ответов нам придется сформировать два SQL-запроса. Вызываться эти запросы будут с помощью компонентов TFDQuery.

Первый запрос выведет список из пяти случайных игроков.

SELECT * FROM Player ORDER BY Random () Limit 5 Собственно, это и будет список вопросов.Второй запрос похож на первый с той лишь разницей, что в этот список не будет попадать один из игроков.

SELECT * FROM Player WHERE Id<>: Id ORDER BY Random () Limit 4 Это список ответов, который будет формироваться для каждого из вопросов. Из списка исключен правильный ответ, который будет вставляться вручную.Давайте внимательно посмотрим на обновлённый дизайнер рабочей области в XE7. Он имеет собственную панель инструментов, расположенную чуть ниже основной. Эта панель содержит две кнопки и два раскрывающихся списка.

092206304d9542b9a189bf19dd3b51fa.jpg

Списки, которые были доступны в ХЕ5 / XE6 — были заменены на список стилей исписок представлений. Кнопки для изменения ориентации устройства и для отображения маски устройства.

Первый из списков позволяет выбрать то, как будет выглядеть интерфейс в процессе разработки.Ко второму списку мы вернёмся чуть позже. А пока разместим на форме необходимые элементы управления и создадим нужный код.

Базовый элемент шаблона TTabControl в нашем приложении будет содержать три вкладки, которые будут переключаться последовательно в процессе теста. Свойству TabPosition присвоим значение None, чтобы пользователь не имел возможности переключать вкладки в произвольном порядке вручную.

На первой вкладке поместим две кнопки. Примерно так, как это показано на рисунке.

2163724e3c314bc0ae954fcf0177c84e.jpg

Кнопка «Выход» закрывает приложение.

procedure TfMain.btnExitClick (Sender: TObject); begin Close; end; Кнопка «Новая игра», как несложно догадаться, инициирует процедуру тестирования.Нажатие кнопки обработаем следующим образом:

procedure TfMain.btnNewGameClick (Sender: TObject); begin qPlayer.Close; qPlayer.Open; // qPlayer.First; TrueCount:= 0; TabControl1.TabIndex:= 1; FormAnswers (4); end; После нажатия на кнопку активной станет вторая вкладка.b7cab0e23b204802bd1795ef616473d0.jpg

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

procedure TfMain.FormAnswerArray (Count, qId: integer); var aPos, i: integer; rb: TRadioButton; BlobStream: TStream; begin Randomize; aPos:= Random (Count — 1);

try BlobStream:= qPlayer.CreateBlobStream (qPlayer.FieldByName ('Photo'), TBlobStreamMode.bmRead); Image1.Bitmap.LoadFromStream (BlobStream); finally BlobStream.Free; end;

qAnswer.Close; qAnswer.ParamByName ('Id').AsInteger:= qId; qAnswer.Open;

i:= 0; while not qAnswer.Eof do begin

if i > Count then Break;

try rb:= TRadioButton.Create (pAnswers); rb.Position.X:= 0; // Image1.Position.X +10; rb.Position.Y:= i * 20; rb.Parent:= pAnswers; pAnswers.InsertComponent (rb); rb.Size.Width:= 250; finally end;

if i = aPos then begin rb.Text:= qPlayerPlayerName.AsString; rb.Tag:= qPlayerId.AsInteger; end else begin rb.Text:= qAnswerPlayerName.AsString; rb.Tag:= qAnswerId.AsInteger; qAnswer.Next; end;

Inc (i); end; end;

procedure TfMain.ClearRadioButtons; var i, j: integer; begin for i:= pAnswers.ChildrenCount — 1 downto 0 do begin if pAnswers.Children[i].ClassNameIs ('TRadioButton') then begin (pAnswers.Children[i] as TRadioButton).Visible:= FAlse; pAnswers.Children[i].Free; end; end; pAnswers.Repaint; end;

procedure TfMain.FormAnswers (Count: integer); begin ClearRadioButtons; FormAnswerArray (Count, qPlayerId.AsInteger); end; Переменная TrueCount будет содержать количество правильных ответов. Процедура FormAnswers — формирует список ответов.Как не сложно догадаться из кода, на панель pAnswers добавляются компоненты TRadioButton, свойство Text которых в качестве значения будет иметь фамилию игрока, а свойство Tag — Id. При этом правильный вариант берётся из результов первого запроса (qPlayer), неправильные — из результатов второго (qAnswer). Положение правильного ответа — случайно.

При нажатии кнопки «Далее» вызывается следующий код:

procedure TfMain.btnNextClick (Sender: TObject); var answID: integer; begin answID:= GetAnswerID; Inc (cnt);

if answID = qPlayerId.AsInteger then begin Inc (TrueCount); ShowMessage ('Верно'); end else begin ShowMessage ('Не верно'); end;

if qPlayer.RecNo <> 5 then begin qPlayer.Next; FormAnswers (4); end else begin TabControl1.TabIndex:= 2; end;

// if qPlayer.Eof then if qPlayer.RecNo = 5 then begin lResult.Text:= 'Верно ' + IntToStr (TrueCount) + ' из 5'; btnNext.Text:= 'Финиш'; end; end; Функция GetAnswerID реализована следующим образом. function TfMain.GetAnswerID: integer; var i, j, k: integer; begin Result:= -1; for i:= tiQuest.ChildrenCount — 1 downto 0 do begin if tiQuest.Children[i].ClassNameIs ('TTabItemContent') then begin for j:= tiQuest.Children[i].ChildrenCount — 1 downto 0 do begin if tiQuest.Children[i].Children[j].ClassNameIs ('TPanel') then for k:= tiQuest.Children[i].Children[j].ChildrenCount — 1 downto 0 do if tiQuest.Children[i].Children[j].Children[k].ClassNameIs ('TRadioButton') then if (tiQuest.Children[i].Children[j].Children[k] as TRadioButton).IsChecked then begin Result:= (tiQuest.Children[i].Children[j].Children[k] as TRadioButton).Tag; Break; end; end; end; end; end; На третьей вкладке просто отображается результат.Новая методология построения мультиплатформенного приложения в Delphi XE7 сводится к тому, что вначале мы размещаем на форме все необходимые компоненты, используя представления Master View. Затем создаём специальное представление для каждой платформы и типа устройства, для которых мы планируем генерировать проект. И наконец, индивидуально настраиваем каждое из представлений.

Всё, что мы делали до настоящего момента производилось в представлении Master.

a23e0dba0d1e40e886d77a103512fc26.jpg

Если сейчас мы запустим приложение на выполнение для Windows (целевую платформу, как и ранее, выбираем в Project Manager), то главная форма будет выглядеть примерно так же, как и в дизайнере.

ab8ae07c09b94ad8af715389085b0a30.jpg

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

Добавим представление для Windows Desktop, просто выбрав его из списка, и подключим стиль. Для этого используем компонент StyleBook.

73dbbce7b08d4f7a9efd5da7bea9e804.jpg

Работающее приложение в Windows будет выглядет примерно так.

b961d5a6ab7c492d9fcacc716c04f9b2.jpg

Так будет выглядеть форма в дизейнере при активном представлении Windows Desktop.

5709e0da7ece4d169292fc5a05881bed.jpg

Но для представления Master изменения не произойдут.

a0e0b2f00e934dc3a0519a99db283cf2.jpg

Важно понимать, что изменения, производимые в одном из представлений, не отразятся на других. Но если вы меняете представление Master, то такие изменения будут «сквозными». Естественно, что при таком подходе существуют определённые ограничения. В упрощённом виде они сводятся к тому, что во всех представлениях на форме должен быть один и тот же набор компонентов. Вы не сможете удалить компонент из одного представления, оставив его в других. Но вы спокойно можете изменять свойства компонентов независимо в каждом из представлений.

Такой подход вносит определённую гибкость. Давайте посмотрим, как механизм FireUI будет работать при создании мобильного приложения для Android.

Прежде всего, нам необходимо будет осуществить перенос базы на мобильное устройство. Для этого используем Deployment Manager, знакомый нам по предыдущим версиям.

8e816efcd7294e058e832d99ef2c24c9.jpg

Как и в предыдущих версиях Delphi, нам потребуется обработать событие BeforeConnection компонента FDConnection.

procedure TfMain.FDConnection1BeforeConnect (Sender: TObject); begin {$IFDEF ANDROID} FDConnection1.Params.Values['Database'] := IncludeTrailingPathDelimiter (System.IOUtils.TPath.GetDocumentsPath) + 'tests.db'; {$ENDIF} end; После этого подключим мобильное устройство и создадим соответствующее представление.ccb2ea15382146549eee5f0c0920d742.jpg

Настроим стиль и расположение контролов и запустим приложение на мобильном устройстве. Приложение принимает совершенно иной вид, но, очевидно, что это одно и то же приложение.

Кроме этого, нам нужно изменить обработчик события BeforeConnection компонента FDConnection.

procedure TfMain.FDConnection1BeforeConnect (Sender: TObject); begin FDConnection1.Params.Values['Database'] := IncludeTrailingPathDelimiter (System.IOUtils.TPath.GetDocumentsPath) + 'tests.db'; end; А для того, чтобы приложение продолжало работать и под Windows, в представлении Windows Desktop просто не будем обрабатывать это событие.ea733ce6db3d4fab91d25c350f10be51.jpg

Итак, в общих чертах мы увидели, какие изменения произошли в методике разработки FireMonkey-приложений с появлением FireUI. Теперь речь идёт не о «единой кодовой базе» и нескольких приложениях для разных платформ, а фактически о едином приложении, которое собирается для каждой из платформ.

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

© Habrahabr.ru