В первой части цикла статей «Firemonkey. От простого к сложному» мы остановились на том, что рассмотрели свойства компонентов Firemonkey, которые присутствуют практически в каждом компоненте FMX, чтобы уже далее к ним каждый раз не возвращаться и сосредоточиться только на уникальных свойства компонентов и работе с ними.
Сегодня продолжим начатую тему и рассмотрим некоторые особенности работы с компонентами, которые расположены на вкладке Standard. Несмотря на то, что 95% всех компонентов, расположенных на этой вкладке нам знакомы ещё из VCL, иногда приходится «по горячим следам», уже работая с компонентом, открывать для себя какие-то новые моменты по работе с ним. Что ж, будем двигаться от простого к сложному и ликвидировать подобные мелкие проблемки.
Раздел 1. Компоненты Firemonkey
2. Списки
Что может быть проще, чем использовать какой-нибудь компонент с этой вкладки? Ведь это та самая первая вкладка на палитре компонентов с которой начинается «программистское детство» — метки с выводом фразы «Hello World!», кнопочка TButton с парой Edit’ов на форме для решения суперсложных квадратных уравнений..ностальгия ё-маё.. Теперь нам придётся снова немного вернуться в «детство», но уже посмотреть на компоненты вкладки Standard более осмысленным взором и в контексте работы с FMX. А начнем мы обзор с компонента TListBox.
Компонент TListBox
Свойство Columns
Давайте посмотрим внимательно на свойства этого компонента. Во-первых, если рассматривать уже известные нам по VCL свойства этого компонента, то мы увидим, что изменилось свойство Columns. Теперь оно по умолчанию имеет значение 1. А вместе с ним и поведение самого компонента TListBox. Вот простой пример.
Создайте в Delphi XE2 новую группу проектов и в этой группе 1 проект — VCL Application, а второй — Firemonkey HD Application. На главные формы проектов бросаем по ListBox’у и устанавливаем им одинаковые свойства Columns = 1. А также заносим в свойство Items..ну скажем по 15 строк.
А теперь меняем постепенно у этих ListBox’ов высоту. Что происходит с ListBox из VCL? Он каждый раз как элементы списка не умещаются по высоте начинает их сдвигать вправо и получается в итоге «что-то с чем-то». Вот, например, какой получился у меня ListBox после того как по высоте стали умещаться всего 3 элемента списка:
Если запустить приложение, то, чтобы добраться до конца списка надо будет четырежды его «проскролить». Конечно, можно настроить Columns как нам надо, самим прорисовывать весь список и т.д. и т.п., но суть не в этом. Смотрим как выглядит FMX.TListBox при точно таких же настройках свойств:
Как видите, FMX.ListBox не делает никаких смещений элементов до тех пор пока не изменится значение Columns. Это мелочь, но такая, которая может в будущем при разработке программ очень пригодится.
Следующее известное нам по VCL свойство, заслуживающее внимания — это свойство Items.
Свойство Items
На первый взгляд может показаться, что работа с этим свойством в FMX.ListBox ничем не отличается от аналогичного свойства у VCL.ListBox. Действительно, мы можем найти в Object Inspector’е свойство Items:TStrings и вызвать знакомый редактор свойства:
Я не зря на скриншоте выше захватил также и окно Structure — обратите внимание на его содержимое. Помните, я говорил, что в Firemonkey каждый компонент — это контейнер для других компонентов? А в Structure никаких дочерних объектов у ListBox’а нет…а они где-то есть =) Получается, что я был не прав? Совсем нет.
Внимание! Фокус.
Сохраняем и закрываем проект. А затем снова открываем. И что мы видим? А вот такую интересную структуру проекта:
Вот Вам и четыре дочерних объекта для ListBox, да и ещё и без имени. Теперь, чтобы получить доступ к их свойствам нам придётся потратить какое-то количество времени и присвоить каждому из дочерних объектов имя и другие свойства. Чтобы не делать эту лишнюю работу, необходимо пользоваться «родным» редактором FMX.ListBox, который вызывается двойным щелчком мыши по компоненту. Выглядит редактор вот так:
В этом редакторе добавляются уже не строки (string), а непосредственно объекты типа TListBoxItem, которые и будут являться дочерними для нашего ListBox. Создаете в редакторе объект, редактируете его свойство Text в Object Inspector и все — никаких лишних движений по поводу изменения свойств вновь появившихся элементов.
После работы со свойством Items можно сделать себе небольшую заметку на память, которая звучит так: при работе с компонентами Firemonkey всегда где это возможно пользуйтесь редакторами свойств, разработанными именно для Firemonkey и старайтесь избегать использования редакторов, которые используются в аналогичных компонентах VCL.
Вполне возможно, что в новой редакции Delphi возможность использования сразу двух редакторов для одного и того же свойства уберут, но пока есть, то что есть.
Но, согласитесь, что в большинстве случаев, при работе с ListBox нам требуется заполнять свойство Items в run-time и тут нам редактор свойства не помощник. В run-time работать с ListBox можно также, как и в случае с VCL.ListBox, не опасаясь, что добавленная строка не будет восприниматься как объект TListBoxItem.
Вы можете записать новый элемент списка так:
begin MyListBox.Items.Add('Строка №1'); end;
и это сработает. И получить N-ый элемент списка так:
var S:string; begin S:=MyListBox.Items[0] end;
то и это тоже сработает. И в этом случае FMX.ListBox будет работать со своими дочерними объектами как со строками, т.е. при записи нового элемента принимать на входе строку и внутренними своими механизмами добавлять в список уже объект TListBoxItem, а при чтении, как показано выше, наоборот — брать элемент TListBoxItem и возвращать из него свойство Text. Проверить это можно довольно просто. Достаточно написать всего две строки кода:
ListBox1.Items.Add('Строка');//записываем строку так как привыкли по VCL ShowMessage(ListBox1.ItemByIndex(0).ClassName);
Во второй строке я воспользовался методом ItemByIndex у FMX.ListBox, который возвращает объект типа TListBoxItem. Отдельный вопрос — как можно сортировать записи в ListBox, но это мы рассмотрим чуть попозже, а пока перейдем к другим свойствам.
Свойство ShowCheckboxes
Это свойство позволяет Вам использовать ListBox так же, как в VCL CheckBoxList, т.е. отображать рядом с каждой записью CheckBox. Включите это свойство и ListBox будет выглядеть так:
Теперь, чтобы обработать событие нажатия CheckBox’а нам достаточно обработать событие OnClickCheck у ListBox, например, таким образом:
procedure TForm8.ListBox1ChangeCheck(Sender: TObject); begin if ListBox1.Selected.IsChecked then ShowMessage('Чекнули элемент '+ListBox1.Selected.Text) end;
Свойство ListStyle
Это свойство определяет расположение элементов списка. Элементы списка TListBox в Firemonkey могу размещаться вертикально (ListStyle = lvVertical) также, как и в VCL, или горизонтально (ListStyle = lvVertical).
При горизонтальном расположении элементов списка свойство Columns будет определять не количество столбцов, а количество строк в списке. При этом каждый элемент списка, в зависимости от значения Columns будет стараться занять всё доступное ему место по вертикали и горизонтали. То есть список, представленный на рисунке выше, но имеющий свойство ListStyle = lsHorizontal и Columns = 1 будет выглядеть вот так:
Свойство AlternatingRowBackground
Свойство, позволяющее использовать альтернативный цвет для чётных элементов списка. Установите это свойство в значение True и получите вот такой красивый список:
Изменить альтернативный цвет строки можно немного подправив стиль оформления элементов формы, но к этому мы приступим ещё не скоро. Пока же можете запомнить, что этот цвет можно изменять.
Свойство ListItems
Это свойство, аналогично уже известному свойству Items для VCL.ListBox возвращает элемент списка по его индексу.
С другими свойствами компонента, которые относятся к свойствам, появившимся только в Firemonkey Вы можете ознакомиться в первой части серии постов «Firemonkey. От простого к сложному».
Перейдем теперь к методам компонента.
[adsenseyu1]
Метод SelectRange
procedure SelectRange(Item1, Item2: TListBoxItem);
Метод выделяет в списке элементы, начиная с Item1 и заканчивая Item2 включительно. Метод работает вне зависимости от того, какое значение имеет свойство MultiSelect:boolean.
Пример выделения первых четырех элементов списка:
ListBox1.SelectRange(ListBox1.ListItems[0], ListBox1.ListItems[3]);
Методы ItemByPoint и ItemByIndex
function ItemByPoint(const X, Y: Single): TListBoxItem; function ItemByIndex(const Idx: Integer): TListBoxItem;
Методы для получения элемента списка по координатам и индексу в списке. Получать объекты TListBoxItem из списка можно только, используя представленные выше методы, либо, используя свойство ListItems.
Методы AddObject, InsertObject, RemoveObject
procedure AddObject(AObject: TFmxObject); override; procedure InsertObject(Index: Integer; AObject: TFmxObject); override; procedure RemoveObject(AObject: TFmxObject); override;
AddObject и InsertObject добавляют новый объект в конец или в заданную позицию списка. RemoveObject — удаляет заданный элемент из списка. В качестве входного параметра для методов может использоваться любой FMX-объект.
Метод Sort
procedure Sort(Compare: TFmxObjectSortCompare); override;
Этот метод позволяет нам сортировать элементы списка по своим собственным критериям. Для этого, в параметрах Sort необходимо передать компаратор, который имеет следующее описание:
TFmxObjectSortCompare = function(item1, item2: TFmxObject): Integer;
Список можно отсортировать по значению строки, записанной в объекте TListBoxItem.
На сегодня закончим (тем более, что опять опубликовал пост раньше времени :( ) В следующий раз продолжим работу с компонентами списков и посмотрим как можно организовать взаимодействие между несколькими списками, используя LiveBindings.
Обсудить статью на форуме
Думаю в статье все таки еще стоит упомянуть, что в текущей реализации списков, создание более 100 элементов чревато неимоверными тормозами. Так как поиск элемента по индексу приводит к полному перебору всех FMX объектов добавленных в список (даже если вы их не добавляли, через сравнение Item is TListBoxItem), и так как отрисовка, обработка нажатий клавиш, кликов и прочего использует ItemByIndex, то время реакции на пользовательский ввод растет в геометрической прогрессии от кол-ва добавленных элементов.
Алексей, вы правы. То же самое заметил и при сортировке списка. Не сказать, что тормоза жуткие, но всё-таки достаточно заметные. Про это я как раз хотел рассказать в конце обсуждения списков, но раз уже тут поднялась тема, то возвращаться к ней уже не буду =)
Будем надеяться, что со временем все устаканится. Статьи понравились. С удовольствием почитаю продолжение.
Так в XE2 есть поддержка Mac OS и iOS, и можно запускать приложения на маке (сам пробовал, получилось:), назрел такой вопрос: можно ли сделать приложение которое потом приняли бы в AppleStore? Там же какие-то сертификаты… Если знаете, расскажите. Спасибо.
mops, про AppAtore ничего не могу сказать, т.к. с маками дела вообще не имел. Пробовал ради интересу виртуалку macOS поставить и собрать чего-нибудь, но то ли виртуалку была фуфловая, то ли я чё-то не понял. В общем даже PAServer не поставился — всё время была ошибка типа «Приложение вызвало исключение…» Поддержка-то есть в XE2 и собирается приложение, но как его «запостить» в AppStore…не знаю. =)
Скорее всего что-то с виртуалкой да, у меня без проблем все с первого раза заработало.
Запостить в AppStore можно, только в начале нужно зарегистрироваться в программе MacOS Developer или IOS developer (в зависимости от того в какой сторе коммититить собираетесь), заплатить $100, отправить несколько документов. А уж потом в личном кабинете на сайте developer.apple.com будете получать сертификаты для постинга в сторе.
Влад, очень интересные статьи ты пишешь.. Всегда приятно почитать. Тоже не давно начал разбираться с Firemonkey. Возник такой вопрос, может быть сможешь помочь.. Вообщем, ввиду задуманного интерфейса нужно мне в Firemonkey приложении использовать фреймы. Сами фреймы отсутсвуют — они VCL. Я пытался создавать дочернюю форму програмно, в качестве родителя(parent) указывая контрол на основной форме(TabControl) и потом вызывая Show(); для дочерней. С VCL-приложениями такая фишка работает, но Firemonkey все равно создает новую форму в виде модального окна. Сталкивался ли ты с реализацией фреймов в firemonkey? или может быть есть какие-то идеи, как это можно имитировать?
ParserYa, спасибо. Всегда рад помочь =) По поводу фреймов сам лично не сталкивался, т.к. не так уж и давно с Firemonkey «общаюсь», но на вкладке Layouts есть пара компонентов — TFramedScrollBox и TFramedVertScrollBox может их использовать?
ParserYA, у меня тоже есть приложения, которые используют TFrame. Пришлось поизучать исходники FMX, чтобы понять как можно сделать эквивалент. Пришел к такому временному варианту. Создал два файла uFrame.pas и uFrame.fmx. (содержимое смотри ниже) Добавил их в проект. А потом когда мне нужны были новые фрэймы, я просто делал так: правой кнопкой мыши кликал на проект->Add New->Other>Inhritable Items>Frame. И потом работал как с обычным TFrame. Правда создавал фрэймы только в Runtime. К сожалению пока не было времени понять, как его зарегистрировать в виде компонента на палитре. Если у кого-нибудь получится — буду премного благодарен. ————- uFrame.pas———————- [code] unit uFrame; interface uses… Подробнее »
Влад, я смотрел эти компоненты — это не совсем то.. Даже если заглянуть в исходники видно, что это простые скролл-боксы.. Может быть, изначально у них и задумывалось им что-то особенное сделать, но потом видимо передумали. Алексей, я вчера полез на форум Embarcadero и там наткнулся на интересные фишки(хотя по сути костыли). Метод заключается в том, что бы положить на форму-псеводфрейм(frame1:TForm1) глобальный блок(Layout1). А в нашей основной форме(fmMain) скопировать объект в какой-то контейнер(LayFrameContainer:TLayout): [code] var f1: Tform1; begin f1 := TForm1.Create(Self); LayFrameContainer.AddObject(F.Layout1); end; [/code] Таким образом, мы не привязываемся не к каким дополнительным классам. Создаем интерфейс/пишем весь код связанный с… Подробнее »
Странно что SelectRange работает с объектами а не индексами. Ну или по крайней мере не имеет перегруженной версии для работы с индексами.
также странно что компаратор для объектов это указатель на функцию, а не анонимный метод, каким он обычно представляется, в частности в дженериках.
зы: про фреймы — в следующем (19й английский) выпуск Blaise Pascel Magazine статья будет про фреймы, там правда тоже костыли, которые не особо мне понравились (:
Подскажите, а что в FireMonkey у форм нет параметров «Минимальная ширина» и «Минимальная высота»? Это только динамически, через onResize что ли отлавливать?
ParserYa, Алексей, если не делать своего наследника TForm, то походу только OnResize использовать, ну а с наследником можно добавить парочку методов и свойств из обычной VCL-формы и будет счастье.
ParseYA: Похоже на то. Я другого не нашел.
не пойму. не могу динамически поменять
не могу поменять динамически курсор
Хорошая статья, автору респект, тоже потихоньку перевожу свои проекты на FireMonkey, занял первое место на конференции благодаря ему) , но возникли небольшие вопросы в связи с этим.. В частности: как в FireMonkey e-mail отправить? Что-то у меня не выходит.. Автору просьба, может написать статью с примерами кроссплатформенного программирования..Отправки тогоже e-mail к примеру.. Думаю не одному мне это будет интересно..)
Bogdan, спасибо. По части e-mail в чем конкретно проблема возникла? При работе с проектом Firemonkey остаются доступными все компоненты Indy — через тот же TidSMTP можно письмо отправить. Или я чего-то не так понял? Поясни плз.
Насколько я понял в XE2 у TidSMTP сменились некоторые свойства.. а разбираться с ними нет времени.. Всёж ещё и учиться надо.. Вообщем, можно небольшой пример, плз, а то как раньше было не прокатывает.. А то не хочется по ошибке выдавать сообщение «Отправте инфу на e-mail», хочется самому отправить… Как раньше было, блин.
[…] и в случае с TListBox, в случае, если это свойство равно True каждая вторая […]
Пытался создать нечто вроде ListView (с динамическим количеством SubItems). Для этого взял ListBox1 зашел в Edit Custom Style. В редакторе стиля сделал следующее: в наш ListBox1 засунул ListBox2 и в ListBox2 поменял свойства ListStyle = lsHorizontal, а Align = alTop. Вроде бы все здорово. Можно в ListBox2 добавлять Items, которая будет играть роль SubItems для ListBox1. Но как получить доступ к Items для ListBox2? Это поле пропадает для ListBox2, к нему есть доступ только в EditCustomStyle. Может кто сможет решить эту задачу?!
Жаль, что у ListItems нет нумератора — нельзя foreach использовать. А хотелось бы.
[…] В FireMonkey все по-другому — по другому организована работа с компонентами, по другому работают стили, по другому реализован […]
Почему-то не работает такая строчка:
for I := 0 to ListBox1.Count-1 do
ListBox1.ItemByIndex(i).TextSettings.HorzAlign:= TTextAlign.Center;
Все элементы всё равно имеют левый алигн.