Delphi programming blog
Источник: http://teran.karelia.pro/articles/item_4499.html
 

Первое использование FireMonkey

Опубликовано 11.12.2011 г. 19:10

Что-то последнее время статьи в блоге появляются не очень то часто, уже пара месяцев прошла с выхода Delphi XE2, а фактически статьей по новым технологиям, которые принес данный продукт так и не появилось. Технологии таких, напомню, две: связывание данных - LiveBindings и новый графический фреймворк FireMonkey. Руки к сожалению не доходят взяться и изучать все новые возможности, но в виду объявленного конкурса по FireMonkey, все таки решил взяться за изучение.

Кстати, говоря о FireMonkey, замечу что 16 декабря пройдет онлайн вэбинар, в котором примет участие главный разработчик и идеолог FireMonkey. Так что это шанс задать вопрос и получить ответ, так сказать "из первых рук". Более подробную информацию по вэбинару можно узнать получить из блога Всеволода Леонова, там же есть и ссылка не регистрацию на вэбинар, и там же можно оставить свой вопрос. Я же в качестве своей задачи для изучения FireMonkey решил переписать программу клиент для сервиса MyShows.ru. Старая свои задачи в принципе решает, но не нравится мне :) Впечатления от FireMonkey сложились неоднозначные. С одной стороны действительно можно легко строить красивые и функциональные интерфейсы, с другой удобство их построения весьма спорно. В комментариях к этой статье в блоге, про установку свойств для нескольких объектов с использованием RTTI многие написали, что свойство указывается в виде строки, так что опечатки и ошибки неизбежны. Однако FireMonkey & LiveBindings используют такой же подход. Все анимации выполняются, например, так:
fs.AnimateFloat('Font.Size', 23);
Интерфейс моего приложения сейчас имеет такой вид:
 
С использованием VCL (без дополнительных компонентов) создать подобный интерфейс, имхо, было бы затруднительно. Потребовались бы дополнительные знания GDI+ или Direct2D. Верхнее меню сделано с помощью простых текстовых элементов TText. В принципе, для того чтобы сделать эффекты анимации при наведении на "пункт меню" вообще не требуется писать код. Каждому такому текстовому элементу я добавил пару эффектов - TGlowEffet - свечение, TFloatAnimation - для анимации размера шрифта, TReflectionEffect для отражений. Установка свойства trigger для эффектов в значение IsMouseMove=true дает нам нужное поведение - при наведении мыши эффекты активируются. Когда мышь покидает элемент, эффекты выключаются. Также для корректной работы необходимо установить значение свойства HitTest элемента в true. Такой обработки мне было недостаточно, активный выбранный элемент необходимо было подсвечивать всегда, поэтому анимацию я переписал вручную. У всех текстовых элементов появился обработчик наведения мыши такого содержания:
procedure TMainForm.WatchingItemMouseEnter(Sender: TObject);
var i : integer;
    fs : TFmxObject;
    ge : TGlowEffect;
begin
    fs := TFmxObject(sender);

    if FActiveShowType  TShowType(fs.Tag) then begin
        TText(fs).Font.size := 20;
        fs.AnimateFloat('Font.Size', 23);

        ge := self.FindChildByClass(fs);
        ge.AnimateFloat('Opacity', 1);
    end;
end;
Если элемент, который вызвал данное событие не является активным (выбранным), то анимируется размер текста и прозрачность эффекта свечения. Для того чтобы находить нужный дочерний элемент (эффект свечения) я добавил generic-метод FindChildByClass.
function TMainForm.FindChildByClass<T>(obj: TFmxObject): T;
var ch : TFmxObject;
    i : integer;
begin
    result := Default(T);
    for i := 0 to obj.ChildrenCount - 1 do begin
        ch := obj.Children[i];

        if ch is T then exit( T(ch) ) ;
    end;
end;
где T имеет ограничение class. Создание меню с анимацией было достаточно простым. В принципе все хорошо и удобно, однако есть одно "но". Когда мы запускаем приложение, и водим мышью по кнопкам, туда-обратно, то загрузка процессора временами достигает 30%. И это для двух ядерного CoreDuo E7400. Т.е выходит что одно из ядер загружается на 60%. Если исходить из того, что вся графика выносится на видеокарту, то выходит что анимация 3-х текстовых (один из 4х пунктов всегда активен и не анимируется) элементов загружает проц? как то имхо странно. Кстати сказать, когда я на форму добавил эффект блура, то приложение вообще практически перестало отвечать, что еще странней, при условии, что для GeForce 9600GT это не сложная задача. Следующим пунктом в моем изучении FireMonkey стало использование списков. Недельку назад прочитал брошюру от Марко Канту про FireMonkey, поэтому получил небольшое представление о возможностях работы со списками. На этом примере очень хорошо рассматривать тот факт, что все контролы в FMX это контейнеры. Контейнеры с той точки зрения, что каждый графический элемент состоит из примитивов, и вы легко можете изобразить такой элемент так, как вам надо. Элемент списка обычно имеет просто текстовую строку. Я же хотел чтобы он отображал для начала картинку, главный текст, и вспомогательный текст. Это делается достаточно просто. Необходимо добавить на форму стили - TStyleBook, и открыв редактор создать новый стиль. Чтобы создать новый стиль, необходимо выбрать контейнер, который будет использовать из палитры инструментов, и перетащить его на область "Create Style". Насколько я понимаю, обычно в качестве основы используют TLayout. Далее я добавил три дочерних элемента - картинку TImage, а также 2 TText. Для каждого из элементов, необходимо задать свойство StyleName. Его мы будем использовать, для обращения к данным компонентам в будущем (но для поиска дочернего компонента можно использовать и свойство BindingName).
 
В первом приближении, о том как работать со стилями мы разобрались. Но редактор стилей, скажу я вам, это просто жесть. На самом деле, это такой инструмент, с которым просто невозможно работать. Самое раздражающее - допустим у меня выбран какой то элемент - текст например. Я изменяю какое то свойство, и жму enter. После нажатия кнопки, активный элемент в дереве сразу же меняется, на какой то другой, непонятно по какому принципу выбранный, и соответственно изображение скрывается с экрана, так что дальнейшие визуальные настройки бессмысленны. Так что после подтверждения каждого свойства приходится заново выбирать элемент. В инспекторе объектов он и не меняется, но сам факт смены объекта просто выбесил меня. Имен у данных элементов нет, так что по инспектору объектов ориентироваться сложно. С отсутствием имен связаны некоторые другие объекты. Во-первых, если вы допустим для картинки в стиле хотите сделать обработчик нажатия мыши, то пока вы не введете имя объекта, всегда будете получать ошибку о невозможности назначения обработчика безымянному элементу. После того как вы зададите имя, обработчик создать можно. Но здесь кроется возможная ошибка. Обработчик добавляется как метод формы. Т.е если мы захотим использовать такой стиль (следовательно и обработчик) в другом месте программы (на другой форме). то это потенциально может привести к AV. Не понятно так же и представление стилей в FMX формы:
  object ShowListStyleBook: TStyleBook
    Resource.Strings = (
      'object _1: TLayout'
      '  Align = alClient'
      '  Position.Point = '#39'(0,33)'#39
      '  Width = 696.000000000000000000'
      '  Height = 646.000000000000000000'
      '  object TLayout'
      '    StyleName = '#39'ShowItemStyle'#39
      '    Align = alScale'
объект называется _1. Откуда берется это _1 и зачем не понятно. Соответственно и обработчики событий по дефолту называются как то так: procedure _1ShowItemImageClick(Sender: TObject); Впрочем, имена компонентов в стилях не сохраняются. Так что тут явная недоработка и неразбериха. В полном отсутствии документации и мануалов это конечно удручает (имеется в виду справочная система Delphi). Нет, там конечно есть описания объектов, их свойств и методов. Но то, что вы изучите свойства и методы TFmxObject не даст вам навыков о том, как работать например со стилями. А это один из краеугольных камней в FMX. Кстати говоря, по сравнению с VCL, если вы добавили пустой обработчик, например, клика, то при сохранении модуля в VCL обработчик удаляется, а в FMX нет. Но, допустим, с горем пополам мы сделали себе нужный стиль для элемента списка. Теперь наша задача применить его к элементами списка. Тут я долго не понимал, почему у меня ничего не работает. Вроде и делал, как в бумажках Марко Канту, или, например, еще здесь есть пример. Мой код заполнения списка был таков:
    procedure AddListItem(title : string);
    var li : TListBoxItem;
        titleText, iText : TText;
        img : TImage;
        i : integer;
    begin
        i := ShowsListBox.Count + 1;

        li := TListBoxItem.Create(nil);
        li.Parent := ShowsListBox;

        li.StyleLookup := 'ShowItemStyle';

        titleText := li.FindStyleResource('ShowTitle') as TText;
        if assigned(titleText) then
            titleText.Text := 'title ' + intToStr(i) +': ' + title;

        iText := li.FindStyleResource('ShowInfo') as TText;
        if assigned(iText) then
            iText.text := 'info ' + intToStr(i) + ': ' + title;

        img := li.FindStyleResource('ShowItemImage') as TImage;
        if assigned(img) then begin
            img.Bitmap.LoadFromFile(Format('%s%d.jpg',[IMG_DIR_PATH, i]));
        end;
    end;
Т.е сначала мы создаем экземпляр TListBoxItem, указываем ему название нашего стиля с помощью свойства StyleLookup. Далее нам необходимо добраться до вложенных элементов стиля - двух текстовых и картинки. Способ приведенный в коде - использование метода FindStyleResource. Хотя на самом деле здесь более уместно использовать метод FindBinding(name : string) : TFmxObject или свойство Binding[] : Variant. Сначала я долго не мог понять, почему у меня ничего не работает, метод FindStyleResource упорно возвращал пустые ссылки. Оказалось всему виной TListBox.BeginUpdate() И EndUpdate() между которыми проводилось добавление элементов, что является обычной практикой в VCL. Надо полагать, что это какая то ошибка. Очевидно, что назначение обработчиков событий для элементов стилей необходимо производить вручную. т.е после того, как я нашел img, используя li.FindBinding('ShowItemImage') as TImage я могу вручную назначить обработчик события OnClick. Если вы посмотрите демо пример по использованию TListBox, то можно заметить там такой вот вариант назначения обработчика, для вложенного элемента (кнопки) стиля:
    Item.Binding['info'] := EventToVariant(DoInfoClick); // set OnClick value 
Поведение кода тут вроде как не совсем очевидно, от части, наверное, потому как я не много работал с Variant'ами. В частности, вроде как Item.Binding['info'] возвращает нам кнопку TButton, а мы ссылке на кнопку присваиваем обработчик (видимо обработчик действия по умолчанию, поскольку имя события мы нигде не указываем). Т.е. с моей точки зрения, мы вообще ссылку на кнопку заменяем ссылкой на метод. т.е теоретически код равносилен такому:
(item.FindBinding('info') as TButton).OnClick := DoInfoClick;
Но у меня как-то с виду впечатление такое не складывается. Но поскольку это все работает, то видимо оно так (: Не совсем понятно, каким образом можно создавать анимацию, например, элемента списка при наведении мыши. Если попробовать определить обработчик события onMouseMove для ListBox, и попробуем получить элемент списка, который соответствует координатам мыши, то такой способ работать не будет, поскольку обработчик не будет получать управление, когда мышь проходит к примеру над картинкой, или текстовой меткой, ну или вообще над TLayout, на котором расположены внутренности нашего TListBoxItem. Аналогично, присвоение onMouseMove для TListBoxItem тоже не дает эффекта. Размер исполняемого файла кстати - 3.5 Мбайт, а при запуске программа занимает 30Мбайт памяти, судя по диспетчеру задач. Возвращаясь к багам редактора можно еще добавить пару моментов: если мы откроем редактор стилей, и закроем вкладку модуля (т.е не редактор стилей, а сразу модуль), то получаем сообщение об ошибке. А при ресайзе окна IDE и следовательно окна редактора стилей, содержимое предпросмотра начинает отображать некорректно, вернее "улетает" рамка выделения текущего компонента. К непоняткам так же относятся следующее - если мы к элементу стиля добавляем анимацию, то она попадает в дерево Структуры формы, и можно потом только гадать к чему этот элемент привязан, - не узнаешь, пока стили не откроешь. Ну и один из увлекательных моментов - в дереве построения стилей, где мы строим иерархию наших элементов которые отвечают за отображение элемента списка, нет возможности перетаскивания элементов. Так что если мне понадобится мое изображение поместить в еще один TLayout, то простите, придется его удалить и пересоздать. Ну или проще поправить в текстовой форме. В общем от первого знакомства впечатления неоднозначные. С одной стороны фреймворк гибкий и с большими возможностями, VCL таких графических возможностей не предоставляет. С другой стороны - отсутствие вразумительной документации и какой то ужастик в виде редактора стилей делает изучение его весьма сложным. Но будем надеяться, что в ближайшее время появятся новые апдейты, в котором будут исправлены не только баги, но и улучшены инструменты для работы.
Метки:  Delphi XE2  |  FireMonkey 

Комментарии

Всеволод Леонов
12.12.2011 в 17:00
Красивый интерфейс получился!
Я даже начал вдруг интересоваться, что есть MyShows.ru
:)
ter
12.12.2011 в 20:53
:))
если сериалы смотришь, то удобный сервис (:
Георгий
13.12.2011 в 13:36
Интересный пост, особенно ваши технические оценки. Видно FMX пока не доведён до "совершенства", есть куда стремится..
Судя из ваших цифр видюха не участвовала. У вас если есть в настройках видеокарты посмотреть температуру. То по ней можно определить загруженность (грубо конечно). У меня с запуском 3D игры с 40 до 80 прогрев.
ter
13.12.2011 в 13:47
у меня при прогреве видеокарты кулер начинает шуметь из за повышения оборотов (: так что можно на звук загрузку оценивать (:
Дмитрий
01.02.2012 в 23:28
Спасибо за статью.
Но вот возникла проблема - не могу назначить панели со стилем(StyleLookup) метод OnClick.
:-(
ter
02.02.2012 в 11:46
в каком смысле не можете?
не срабатывает обработчик? если так, то убедитесь что используемые в стиле компоненты не используют HitTest, иначе до панели клик не дойдет.

*нет Xe2 под рукой, так что глянуть получится только вечером.
Дмитрий
02.02.2012 в 13:32
Убрал - заработало. Спасибо большое)
ter
02.02.2012 в 14:52
не за что (:
Дмитрий
03.02.2012 в 14:26
Вот еще, не знал где спросить, поэтому извиняюсь, если пишу не там.
Компонент Memo в Fmx.
Когда при запущенном приложении ставлю фокус на компонент(тыкаю по нему мышкой) происходит глюк с графикой, т.е. какие-то синие искаженные полосы, да и компонент сливается с формой, пытаюсь печатать текст - вис, экран моргает, выходит сообщение что был перезапущен графический что-то там, извиняюсь, забыл.
Как только поставил ХЕ2, переустановил драйвера, и во время первого запуска ХЕ2 с программой с memo все было хорошо, но вот сейчас же =(
Radeon HD 4870 1gb
ter
03.02.2012 в 18:09
попробую вечером у себя, как то я Memo еще не использовал (:
обновления для ХЕ2 установлены? их там 3 штуки было, скоро 4е выходит.
драйвера видеокарты последние стоят (ну в смысле не совсем старые ли) ?

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

если на пустом приложении с мемо возникает такая же проблема, то тут надо уже грешить либо на сам FMX, либо на дрова для карты, либо хз на что.
а если на пустом все нормально, то проблемы искать уже в своем приложении надо.
Дмитрий
03.02.2012 в 18:46
Ошибка возникает на пустом приложении. Стоит апдейт 3.
Насчет драйверов не уверен, были проблемы установкой самых последних - тупо не ставились.
Сейчас попробую пошаманить с драйверами. Спасибо)
ter
03.02.2012 в 18:34
по мне дак любые драйвера за последние пару лет должны работать нормально.
какая кстати ОС? тоже немаловажно, ибо на хп используется GDI+ на вин7 - Директ2Д (по уверениям разработчиков по крайней мере)
еще мб разница в 32/64бит.
Дмитрий
03.02.2012 в 18:30
XP SP3 32б
Поставил драйвера с диска от видеокарты - никаких глюков с Memo.
Удивительно.
Но так же было и когда поставил предидушие драйвера.
Подождем, поглядим. Спасибо Вам)
Kongressman
24.01.2013 в 11:10
Здравствуйте! У меня проблемы с memo, я хочу сделать копирование/вставку/вырезание/удаление выделенного текста при нажатии на кнопку, но натыкаюсь на туеву хучу ошибок. я пишу с телефона и не могу предоставить код, но он простой до жути, но тем не менее не работает в firemonkey :(
можете помочь?
teran
24.01.2013 в 22:04

попробовал в XE3. На форме Memo1 и 3 кнопки с tag = 0,1,2 - копировать, вызерать, вставить. Обработчик кнопок следующий:


procedure TForm2.MemoActionButtonClick(Sender: TObject);
type TMemoAction = (maCopy, maCut, maInsert);
var ma : TMemoAction;
    txt : string;
begin
    ma := TMemoAction(TButton(sender).Tag);
    case ma of
        maCopy : ShowMessage(memo1.SelText);
        maCut :  memo1.DeleteSelection();
        maInsert : begin
                txt := InputBox('caption!', 'input string', '');
                memo1.InsertAfter(memo1.CaretPosition, txt, [TInsertOption.ioMoveCaret]);
          end;
    end;
end;

все работает вроде. Единственное что теряется, это подсветка выделенного текста когда memo теряет фокус.

Прикладной
09.10.2016 в 21:53
Отличный материал подойдет не только новичкам, но тем, кто решит создавать мобильные приложения
- Имя
- e-mail*
- Сайт
вы можете использовать теги [i],[b],[code],[quote]
Дополнительно