уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.

black_coolfaceО, ужас! Конец Света уже сегодня, уже сейчас, а ещё столько всего не сделано, столько не написано! Я не успел увидеть в действии Mobile Studio, не построил дом…книгу написал, правда. И как же быть? Чем себя занять те несколько часов, которые отделяют нас от Большого_Всемирного_Облома?!

Шутка конечно :) Какой Конец Света, какие такие майя? Завтра проснемся, возьмем лопаты клавиатуры и пойдем работать. Но всё же, раз уж кто-то там чего-то нагадал, сегодня я решил (как и полагается перед значительным событием) как следует подготовиться и заняться благостным «ничегонеделаньем» — поиграться в очередной раз с FireMonkey. Не поработать, не по-изучать что-то в FMX 2.0., а просто поиграть, а заодно и показать тем, кто ещё не встречал FMX  в действии (такие же ещё есть, да?) то, как можно быстро накидать небольшой красивенький интерфейс для своей программки, используя только простейшие компоненты FireMonkey с вкладок Layouts и ShapesИтак, что мы знаем о FireMonkey? За два года кто-то из нас уже успел изучить эту платформу «от А до Я», залезть в «кишки» платформы и выудить от туда много чего интересного и не очень.

Думаю, что многие из тех, кто знаком с FMX не понаслышке, согласятся со мной в одном: FireMonkey — очень гибкая платформа. И разрабатывать интерфейс программы в FireMonkey, даже самый «навороченный», с точки зрения всяких эффектов и анимации, довольно просто. Любой элемент интерфейса будь то кнопки, списки или меню мы, при желании, можем переделать как угодно и во что угодно. А можем собрать и свои собственный аналог компонента, например, быстро собрать из примитивов простенькое меню для программы. Например, на то, чтобы накидать вот такое меню (без всяких bpl и собственных классов) у меня ушло чуть больше 20 минут:

[youtube_sc url=»http://www.youtube.com/watch?v=D8da5o96foQ» width=»400″ theme=»light»]

И состоит это меню всего-то из нескольких TLayout, нескольких TText и двух эффектов (TEffect) Firemonkey. Ну, а чтобы сменить эффект появления подменю на чуть другой вообще минут пять от силы и, в итоге, получилось такое меню:

[youtube_sc url=»http://www.youtube.com/watch?v=BgjmyZmeVSs» width=»400″ theme=»light»]

Да это не компонент, который можно положить на форму и настроить по своему желанию, но тем не менее на начальном этапе работы с FireMonkey может помочь разобраться с самыми простыми методами FMX.

Что необходимо, чтобы создать подобное меню для своего приложения?

Во-первых, представить себе то, из чего состоит такое меню и как оно работает. Меню у нас двухуровневое — есть главные элементы, которые видны всегда пользователю и есть подменю, которые выезжают, выпадают, вылетают (нужное подчеркнуть) при клике по главному элементу. При этом подменю выравниваются по правому отступу своего «родителя». Главный элемент при клике по нему подсвечивается (или иным образом выделяется среди остальных), а все элементы в целом, при наведении на них курсора мыши, отбрасывают тень. Вот и вся работа. Ну, а что уж будет происходить при клике по подменю — зависит от назначения программы.

Во-вторых, свой новый элемент интерфейса, используя то, что уже есть в FireMonkey.

Для представленного выше меню я сделал такой каркас:

1. Бросил на форму TLayout и установил ему свойства:

  • Align=alTop.
  • Name=’MenuLayout’

2. На MenuLayout бросил ещё два TLayout и установил им такие свойства:

  • Первый layout (для главных элементов меню):
    • Align= alTop
    • Name = ‘MainItems’
  • Второй layout (для подменю):
    • Align= alClient
    • Name = ‘SubItems’

Таким образом я уже получил некое подобие каркаса для будущего стиля (если он понадобиться) 3. Для теста бросил на MainItems четыре компонента TText, задал им свойство Text («MainItem…»), Name = «MainItem…» а также следующие одинаковые свойства:

  • Align = alLeft
  • Padding.Right = 3

4. Аналогичным образом бросил на SubItems 3 TText, которым задал только свойства Text и Name, а Align оставил по умолчанию. 5. На первый элемент TText бросил два эффекта:

  • TGlowEffect — для подсвечивания нажатого элемента меню
  • TShadowEffect — для выделения элемента меню над которым находится курсор мыши

У обоих эффектов свойство Enabled было выставлено в False. На этом разработку прототипа нашего будущего меню можно считать законченной. Теперь приступим к коду. Начнем с эффекта выделения пункта меню (TShadowEffect). Этот эффект должен работать в случае, когда курсор мыши находится над элементом меню. Так как он у нас всего один на все семь элементов меню, то этот эффект надо будет во время перекидывать с одного элемента на другой. Можно написать вот такой обработчик OnMouseEnter для любого элемента меню (TText):

procedure TForm11.MainItem1MouseEnter(Sender: TObject);
begin
  if Sender is TText then
    begin
      TFmxObject(Sender).AddObject(ShadowEffect1);//перебросили эффект
      ShadowEffect1.ApplyTrigger(TFmxObject(Sender),'IsMouseOver=true');//установили триггер для включения эффекта
    end;
end;

Теперь проходимся по всем элементам нашего меню и в Object Inspector’е назначаем их событиям OnMouseEnter этот обработчик. Все. С первым эффектом разобрались. Можете запустить приложение и убедиться, что эффект «перескакивает» с элемента на элемент и работает.

Теперь разберемся с анимацией. Рассмотрим, например, анимацию, показанную на втором ролике. Если рассматривать эту анимацию по шагам, без оптимизации кода, решая задачу, что называется «в лоб», то код будет таким:

{уводим все подменю за пределы формы - они будут в итоге - справа}
SubMenu1.Position.X:=(SubMenu1.Parent as TLayout).Width;
SubMenu2.Position.X:=(SubMenu1.Parent as TLayout).Width;
SubMenu3.Position.X:=(SubMenu1.Parent as TLayout).Width;
{показываем элемент}
SubMenu1.Visible:=True;
{"выталкиваем" 1 подменю}
SubMenu1.AnimateFloatWait('Position.X',TText(Sender).Position.X, 0.5, TAnimationType.atOut,TInterpolationType.itBounce);
{"выталкиваем" 3 подменю}
SubMenu2.Visible:=True;
SubMenu2.AnimateFloatWait('Position.X',SubMenu1.Position.X+SubMenu1.Width, 0.5, TAnimationType.atOut, TInterpolationType.itBounce);
{"выталкиваем" 4 подменю}
SubMenu3.Visible:=True;
SubMenu3.AnimateFloatWait('Position.X',SubMenu2.Position.X+SubMenu2.Width, 0.5, TAnimationType.atOut, TInterpolationType.itBounce);

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

  1. Выставить позицию элементов подменю так, чтобы они находились за пределами видимости
  2. По очереди с первого по последний элемент анимируем свойство Position.X, делая задержку перед началом «выталкивания» очередного элемента.

Теперь приведем код для анимации элементов меню в порядок. Как узнать, что какой-либо TText на слое SubItems является дочерним по отношению к какому либо элементу на  слое MainItems? Сделать это можно, используя, например, свойства Tag. Так, TFmxObject содержит сразу три подходящих для нас свойства:

property TagObject: TObject read FTagObject write FTagObject;
property TagFloat: Single read FTagFloat write FTagFloat;
property TagString: string read FTagString write FTagString;

и плюс ко всему у нас так и осталось в распоряжении свойство:

property Tag: integer;

Ок, для примера воспользуемся простым Tag. Добавим в наше меню ещё несколько элементов TText и зададим им свойства Name и Text. У меня получилась следующая менюшка:

Меню из TText

Меню из TText

Теперь все элементам TText со слоя MainItems назначаем свойства Tag от 0 до чего-то там (у меня последний элемент будет иметь свойство Tag равным 3). Всем элементам со слоя SubItems назначаем Tag в пределах от 0 до X (где X — это Tag последнего элемента меню). Таким образом мы определили к какому элементу меню какие подменю относятся.

Следующий шаг — определить необходимые нам элементы подменю. Сделать это теперь можно очень просто. Я, например, написал вот такую простенькую функцию:

type
  TSubMenuItems = array of TText;
 
function TfMain.GetSubItems(AItemTag: integer): TSubMenuItems;
var
  i: integer;
  Child: TFmxObject;
begin
  Result := nil;
  {проходим по всем "деткам" слоя SubItems} 
  for i := 0 to SubItems.ChildrenCount - 1 do
  begin
    Child := SubItems.Children[i];
    {Нашли TText}
    if Child is TText then
    begin
      {Если Tag совпадают - оставляем элемент видимым и 
       смещаем его за пределы формы и вносим в массив.
       Если Tag не совпадают, то просто скрываем элемент}
      TText(Child).Visible := Child.Tag = AItemTag;
      if TText(Child).Visible then
      begin
        SetLength(Result, Length(Result) + 1);
        Result[Length(Result) - 1] := TText(SubItems.Children[i]);
        Result[Length(Result) - 1].Position.X := SubItems.Width;
      end
    end
  end;
end;

Все необходимые элементы подменю получены. Осталось сделать им анимацию. Пишем ещё один метод:

procedure TfMain.AnimateSubMenu(AItem: TText; ADelay: single;
  Interpolation: TInterpolationType);
var
  i: integer;
  ASubItems: TSubMenuItems;
begin
  ASubItems := GetSubItems(AItem.Tag);
  if Length(ASubItems) > 0 then
  begin
    {"вытолкнули первый элемент"}
    ASubItems[0].AnimateFloatWait('Position.X', AItem.Position.X, ADelay,
                                   TAnimationType.atOut, Interpolation);
    {"выталкиваем" все остальные, если они будут}
    for i := 1 to High(ASubItems) do
      ASubItems[i].AnimateFloatWait('Position.X', ASubItems[i - 1].Position.X +
                                                  ASubItems[i - 1].Width, 
                                     ADelay,  
                                     TAnimationType.atOut, 
                                     Interpolation);
  end;
end;

Осталось вызвать этот метод в нужном месте. Вызывать будем, естественно, на OnClick главного элемента в меню и там же мы будем использовать второй эффект (TGlowEffect). Пишем такой обработчик OnClick у первого элемента TText на слое MainItems:

procedure TfMain.MainItem1Click(Sender: TObject);
begin
  GlowEffect1.Enabled := False;
  if Sender is TText then
    begin
      if not GlowEffect1.IsChild(TText(Sender)) then
        TFmxObject(Sender).AddObject(GlowEffect1);
 
      GlowEffect1.Enabled := True;
      AnimateSubMenu(TText(Sender), 0.5, TInterpolationType.itBounce);
    end;
end;

Назначаем обработчик всем остальным элементам TText со слоя MainItems, запускаем программку и убеждаемся, что все работает. Что мы ещё забыли сделать? Да, пожалуй только выставить всем TText на слое SubItems свойство Visible в False.

Как видите, при работе над меню мы использовали только самые простые компоненты FireMonkey — Tlayout с вкладки Layouts, TText с Shapes и всего два эффекта с вкладки Effects. Сам код получился небольшим по объему.

Теперь можно украшать и дорабатывать это меню как угодно — добавить какие-нибудь картинки, создать собственные классы для элементов меню и т.д. Но это уже отдельная тема — создание полноценных компонентов в FireMonkey.

0 0 голоса
Рейтинг статьи
уважаемые посетители блога, если Вам понравилась, то, пожалуйста, помогите автору с лечением. Подробности тут.
Подписаться
Уведомить о
15 Комментарий
Межтекстовые Отзывы
Посмотреть все комментарии
Алексей
17/01/2013 12:55

Спасибо за интересную статью, все никак до Firemonkey не доберусь, уже вторая версия вышла, а я так и не попробовал, все на vcl сижу.

Kongressman
Kongressman
24/01/2013 17:45

Привет! можешь мне помочь с memo, а точнее с «копировать/вставить/вырезать/удалить выделенный текст» при нажатии на кнопку. Обычным способом это сделать не получается:(

Kongressman
Kongressman
24/01/2013 18:21

pcg. 3dn. ru/publ/2-1-0-3

я сейчас пишу с телефона и не могу показать код, но делал я все по этому сайту ^

Kongressman
Kongressman
24/01/2013 21:21

Спасибо, я уже разобрался!!! :)
Оказалось все намного проще:
в коде пишем Memo1. дальше выпадает список функций и пр. и в нем есть CutToClipboard, CopyToClipboard и другие… все это работает именно с выделенным текстом!

Kongressman
Kongressman
25/01/2013 00:33

Но это еще не все:( есть проблема намного тяжелее этой, надеюсь вы мне поможете…

Я делаю текстовый редактор и мне нужно в Image загрузить именно из файла картинку, я делаю так …LoadFromFile(‘Im.png’); . Все работает нормально, пока с помощью диалога открытия я не открываю любой файл (С:/TEXT.txt)…
Далее при попытке опять загрузить картинку выпадает ошибка «Нет файла C:/Im.png»
Видите? Это папка текста! Почему файл ищеться здесь?

Kongressman
Kongressman
25/01/2013 00:41

Можно как-то прямо в программе узнать в какой папке лежит .exe файл (и записать этот путь в переменную)? и из этой папки загружать все необходимые файлы, учитывая то, что этот путь будет менятся?

Вобщем, как узнать в какой папке лежит программа и записать путь в переменную?!! бьюсь над этой проблемой уже очень давно…

Kongressman
Kongressman
25/01/2013 09:47

Спасибо огромное! сделал все так, как хотел:) это пока все:)…

Slava
Slava
13/05/2013 06:12

Не могу повторить урок Меню из простейших компонентов.
Получаю ошибку в строке процедуры if Length(ASubItems) > 0 then

Slava
Slava
13/05/2013 06:49

С ошибкой в строке if Length(ASubItems) > 0 then
стало понятно как только увидел свой же комментарий. Просто в коде статьи на сайте знак больше
выведен спецсимволом html, сразу не догадался.
Но одна ошибка при компилляции все равно остается. Это строка if not GlowEffect2.IsChild(TText(Sender)) then
Что можно предпринять?

Slava
Slava
13/05/2013 18:57

Errors: TGlowEffect does not contein named IsChild
Message: E2003 Undeclared identifier IsChild
Версия RAD Studio XE2 без обновлений.
И наверное, дело обстоит так, как вы и сказали.