ВВЕДЕНИЕ
Язык программирования Delphi представляет собой объектно-ориентированный высокоуровневый язык программирования, направленный на написание прикладного программного обеспечения и является диалектом языка Pascal. Возможность легко и просто создавать приложения для персональных компьютеров на платформе Windows с прекрасным дизайном (типа Microsoft Office) снискала любовь к нему многих.
Вышедшая в 2011 году версия Delphi XE2 добавила компилятор Win 64 и поддержку операционных систем фирмы Apple (MacOS X, iOS), а в 2013 году Delphi XE5 позволила создавать приложения для устройств на платформе Android. В настоящее время наиболее приемлемой, видимо, является Delphi 10.3, в различном исполнении. Только что появившаяся Delphi 11 исправила некоторые (далеко не все) ошибки предыдущей версии и ускорила работу.
Работа по созданию кросплатформенных приложений, однако, имеет много особенностей, а каждая версия, по существу, заново переписывает многие свойства и функции, так что руководства для XE5, 6, 10 очень часто дают неверную информацию и приводимые примеры зачастую не работают. К тому же, новостью будут как новый язык, название и язык которого FireMonkey, так и его новые подходы.
Все это затрудняет работу с Delphi 10, 11, которые являются, видимо, наиболее приемлемыми из современных вариантов Delphi для создания Android приложений, но все еще имеющими множество багов и особенностей работы, которые как раз и будут описаны в данной книге, которая будет интересна как знакомым с Delphi, так и новичкам, так как приводятся все основные функции и примеры работы программ для Android.
1. DELPHI 7 ИЛИ DELPHI 10 и 11?
Для тех, кто ориентирован на создание красивых, мощных и быстродействующих программ для персональных компьютеров платформы Windows, выбором, видимо, является версия Delphi 7, к которой вернулись большинство программистов из более поздних версий. Она является, по мнению большинства, наиболее стабильно работающей и наиболее дружественной программой, на которой можно создавать практически любые прикладные программы для персональных компьютеров и имеющей ряд библиотек.
Интересно, что при создании кросплатформенных приложений одновременно с вариантом для Android создается и вариант для Windows, причем значок приложения позаимствован у Delphi 7.
Различие программ созданных на Delphi 7 и 11, определяется различием платформ и, главным образом, экрана. Экран персональных компьютеров не только больше, но и ориентирован как «Ландшафтный», тогда как «Портретный» экран смартфона, вытянутый практически в 2 раза к ширине, не дает возможности каких-либо украшений и даже надписи приходится делать мелкими и плохо читаемыми. Также в Android варианте недоступны многие функции, привычные для Delphi 7, например, популярное «Меню» отсутствует, как и текстовый редактор «RichEdit», таблицы совершенно изменены, а графики урезаны до минимума. Таким образом, лучше использовать дружественный отшлифованный интерфейс Delphi 7 для ПК, тогда как только необходимость создания приложения для смартфона делает нужным использование Delphi 10.3.
Важнейшим преимуществом Delphi 7 является мощный аппарат для создания графиков, не уступающим Excel, которые удобно и просто создавать и сохранять в удобном формате.
Также наличие в Delphi 7 компонента RichEdit позволяет форматировать текст практически как в Word и сохранять его в формате *.rtf.
Огромное количество дополнений к Delphi 7 содержит сотни компонентов практически на «любой случай» и позволяет создавать мощные, красивые и удобные программы, которые невозможно получить в среде Android.
Из рисунка видим, что для ПК имеется и меню, и больше возможностей обработки текста, сохранения в файл, распечатки, большой экран и большой текст, а также обычные значки на кнопках для которых на смартфоне часто нет места.
Скопировать в текстовый редактор (или в e-mail себе для передачи в свой ПК) можно в обоих приложениях, а скрин экрана доступен в Android как функция платформы и может затем использоваться и передаваться как обычное фото. В последнее время также Delphi 10, 11, имеют возможность обойти запрещения для современных смартфонов сохранять файлы во внешних папках: Downloads и Documents, для чего потребовалась целая серия команд и специальный запрос подтверждения прав для такого действия.
2. К НАЧАЛУ МУЛЬТИПЛАТФОРМЕННОГО ПРОГРАММИРОВАНИЯ
2.1. ОСОБЕННОСТИ DELPHI ANDROID
Наиболее отличающимся от обычного Delphi, видимо, является язык и библиотека «FireMonkey», которую авторы поднимают до уровня платформы. Задумка новой платформы была в возможности кросс-платформенного кода, одинакового для разных платформ, от Windows до Android, и значительное расширение возможностей: введение дополнительных наборов классов компонентов и сервисных интерфейсов, написанных на языке Delphi, в том числе для 3D-приложений и высококачественной графика.
На деле, однако, при реальной разработке приложений для Android сразу приходится сталкиваться с отсутствием некоторых привычных и важных компонентов (меню, RichEdit, урезанных таблиц, отсутствием ряда свойств, например, цвета и названия у Панели, неприглядного изначального вида Кнопок, едва видимого Edit и многого другого).
К тому же, нестабильность работы приводит к тому, что пропадают ряд свойств компонентов, вдруг перестают работать элементарные функции, время работы Timer растягивается в 2 раза, на сохранение файлов накладываются ограничения и запреты самого смартфона, в зависимости от экрана смартфона компоненты приложения могут вылезать за пределы экрана, а функция скроллинга не включаться, что не дает возможности в Memo прочесть текст до конца, клавиатура накладывается на компоненты и не видно, что записывается, и пр., включая часто крайне медленную работу приложений и их большой объем.
Тем не менее, возможность создавать работающие приложения для Android имеется, как и возможность выкладывать их в мировую сеть через Google Play Market, а для привычных в программировании к Delphi это оптимальный вариант создания APK программ.
Для работы со многими устройствами смартфона и даже доступа к внешним файлам нужны специальные разрешения в манифест.
2.2. НАЧАЛО РАБОТЫ И СОЗДАНИЕ ПРОЕКТА (ПРИЛОЖЕНИЯ)
Для начала работы следует скачать с официального сайта или найдя в Яндекс или Google, установочную программу Delphi 11 в любом варианте. Запустить установку — это все, что требуется, в отличие от других программ, все происходит само, с созданием значка на экране и открытием самой про граммы.
При открытии мы видим весьма заставку, которую спокойно можно опустить и сразу открыть файл создания приложения: файл — новый — мультиплатформенное приложение — один из полуготовых вариантов (обычно открывают «Blank Application»: пустую заготовку).
Сразу создается, как обычно, форма (Form1), на которой и будут располагаться различные компоненты работающей программы.
Здесь же видны основные части программы для работы с компонентами.
Слева: Structure (Структура взаимоподчинения компонентов, пока что представлена одной Form1), Object inspector (Инспектор объектов для выбора свойств объектов).
Справа: Pellete (Палитра сгруппированных компонентов) и отображение Project Group c наиболее важными функциями: Build Configuration (Debug и Realese) — для окончательной конфигурации и выпуска программы.
Вверху: ряд кнопок для управления, наиболее важны: File — для создания, сохранения и открытия приложения; Project — с функцией Option для настройки приложения (значок, вид и ряд др.) и зеленые кнопки запуска работы программы (с предварительным debug и без него).
Запуск программы для промежуточных уточнений и исправления ошибок (которые всегда имеются) реально проводить можно и нужно сразу же на подсоединенном к ПК смартфоне (в режиме «разработчика»), поэтому первым делом такой режим нужно настроить, предварительно выбрав в 3-х верхних окошках режим «Android» и «Master» (для кросплатформенного приложения).
2.3. НАСТРОЙКА СМАРТФОНА ДЛЯ РАБОТЫ С СОЗДАВАЕМЫМ ПРИЛОЖЕНИЕМ
Для входа в режим разработчика нужно войти в «Настройки» смартфона и в разделе «О телефоне» нажать несколько раз на последний пункт «Номер сборки» для появления сообщения
«Вы стали разработчиком», затем перейти в раздел «Система» и «Дополнительно», где появился пункт «Для разработчиков», нажав на который пройти почти до конца до пункта «Отладка» и выбрать флажок «Отладка по USB». Теперь, подключив смартфон к компьютеру, мы увидим вверху в окошке название смартфона.
2.4. ОСНОВНАЯ ФОРМА ПРИЛОЖЕНИЯ (Form1)
При начале работы с созданием приложения, сразу нужно определиться с несколькими моментами. Во-первых, сразу изменить размеры и форму Form1, в соответствии с формой смартфона. Так как размеры смартфона практически соотносятся 1:2 можно выставить размеры, например: 360*700. Все остальные настройки можно сохранить неизменными. При желании можно изменить цвет формы (свойство Fill — Color; а также получить градиентную заливку: Brush — Gradient, свойство gradient должно быть и в Kind и в Gradient).
На специальной панели Brush Designer с помощью 2-х ползунков можно выставить любой цвет и градиент, а также повернуть градиент на 0 — 180о. Можно также поставить галочку для Full Sсreen и для Position выставить Screen Center. В Events для Form есть множество действий, важнейшее onCrate: действия при создании формы. Свойство Quality: качество прорисовки объектов/производительность.
После создания Формы на нее перетаскиваются из Палитры все необходимые компоненты: Кнопки (Button), Метки (Label), Окошки (Edit), компоненты для отображения текста (Memo, при том, что RichEdit не доступен для Android) и Базы данных (Таблица: в Delphi 10.3, 11, два типа таблиц — обычная StringGrid для строчных данных и специальная Grid для любых типов данных). В основном используются компоненты из групп Standard и Grids. Широко используются компоненты Panel и Layout как контейнеры, обычно для выравнивания других компонентов, которые на них располагаются.
2.5. СОХРАНЕНИЕ И ЗАПУСК РАБОТЫ ПРИЛОЖЕНИЯ
На первом этапе создания приложения следует его сохранить в выбранной папке (сделайте папку с понятным названием), при этом все директории и папки не должны иметь русских букв (хотя сейчас это уже не обязательно). На этапе создания приложения и перед его запуском следует его сохранять. Сайт для загрузки Delphi11: https://www.embarcadero.com/
Для проверки программирования можно в верхнем меню Project выбрать Build Project, при наличии ошибок он выдаст анализ программы и выделит ошибки с указанием на них и описанием в нижней панели. Аналогично, так можно заставить (не всегда) самостоятельно заполнить Uses, если вдруг выдается предупреждение, что данная функция отсутствует!
Подключив смартфон можно, нажав на зеленую кнопку Run увидеть как работает программа на настоящий момент. Обработка, передача и запуск программ идет достаточно долго, что приводит к выдаче предупреждения о невозможности запуска в виду уже загруженности устройства, не обращайте на это внимания.
При запуске программы создается обычный для Android файл APK, который располагается в: Android/Debug (Release) /Имя/bin/ ИмяПроекта. APK. Почему-то чаще в Debug. Просто компилирование программы через Project/Build и через Project/ Compile Project файл APK не создает!
2.6. ПОДГОТОВКА ПРИЛОЖЕНИЯ К ВЫПУСКУ
Для окончательной готовности приложения следует провести ряд действий:
— Перейти в Project — Option — Version Info и изменить название программы (по умолчанию равна названию проекта) — пункт label.
— На Uses Permissions можно выбрать перечень разрешений запрашиваемых от системы (изначально почти все пусты!), но лучше так все и оставить; ряд разрешений уже стоит при создании приложения изначально: сохранение и загрузка файла из внешних источников, camera, internet и ряд др., однако, реально они работают только при программном создании разрешения, иначе блокируются смартфоном как небезопасные, что оказывается довольно сложным делом.
— Можно сменить Значок — пункт icons (создать и загрузить несколько — от 36x36 до 144x144).
— Также в разделе Otientation можно выбрать Portrait, или оставить так — если Вы готовы сделать два приложения для разной ориентации экрана.
Наконец, нужно создать Подпись — Сертификат: Project — Options — Provisioning — готовность к работе изъявит кнопка New Keystore, на щелчок открывается помощник по созданию хранилища ключей: название файла хранилища (*.keystore), пароль доступа к хранилищу, псевдоним ключа (ключевую пару открытого и секретного ключа), пароль к псевдониму и период действия ключа (по умолчанию около 9000 суток). После завершения по лучим требуемый сертификат, предназначенный для подписи файла.
Для выкладывания приложения в Google Play нужно найти страницу разработчиков Google; ввести аккаунт (например, который использовали при регистрации своего устройства на базе Android) и небольшую плату.
После прохождения платежа окажетесь в своём кабинете, позволяющем загружать на Google play приложения.
Завершив создание профиля разработчика, получаем возможность отправить своё приложение на сервер Google, процесс начинается с щелчка по кнопке Upload Application.
После загрузки нужно заполнить профайл приложения, даже поля, помеченные как необязательные (поможет Google продвигать приложение на рынке).
Таким образом, созданное приложение готово и его можно распространять практически по всему миру (для чего оно должно быть мультиязычно, что также позволяет делать Android.
3. ОСОБЕННОСТИ КОМПОНЕНТОВ ANDROID
Главные особенности при программировании для Android связаны с различиями смартфонов — диагонали и разрешения экрана, что сбивает настройки компонентов и может вести к выходу их за пределы экрана — используйте Scale для компонентов.
Также широко используются компоненты палитры LayOut, особенно GridPanelLayout и FlowLayout — компоненты располагаются рядом с фиксированным расстоянием, изменение одного отодвигает другие; GridLayout — сетка, обычно используется для нескольких одинаковых компонентов. В редакторе Structure следует перетащить мышью компонент в подчинение Layout. Такой прием используется и для придания свойств компонентам с палитры Effects. Компонент Panel также широко используется как контей нер для других компонентов, но практически не виден, а использование Effects часто приводит к непредсказуемым эффектам вплоть до отказа работы.
Также используется и Плавающая верстка — привязывая один компонент к границам другого методом Align, при этом важна последовательность создания компонентов и их Align.
Внутри контейнера используется свойство Margin (и Padding) для фиксирования расстояния от границ контейнера, а также свойство Position (X — от левого края, Y — от верхнего).
В Delphi 11 широко используются различные стили, в частности, общий стиль формы Form1 для всех компонентов. Он, однако, дает слишком большой шрифт, поэтому практически в StyledSetting отключают все компоненты стиля и в TextSetting устанавливаем заново для каждого компонента ему подходящий. Рекомендуется шрифт Tahoma для обычного текста, 14—16 для заголовков и 10—11 для компонентов типа Таблиц, когда нужно загрузить большое количество данных. Свойство WordWrap (при этом должно быть свойство AutoSize:= false) — автоперенос на другую строку не вмещающегося текста следует определить как true. Также часто нужно установить позицию текста center (изначально leading).
Приложения FireMonkey автоматически отображают свой соответствующий собственный стиль во время выполнения, без необходимости добавления сборника стилей или кода. Кроме того, приложения FireMonkey могут иметь пользовательские стили. Чтобы применить определенный стиль формы можно загрузить его из файла (добавив в Uses: FMX.Styles.):
TForm1.FormCreate (sender: TObject);
TStyleManager.SetStyleFromFile('Air.style’);
Для компонентов имеется ряд свойств стиля, например, свойство прозрачность: Opacity, по умолчанию 1.0 — полная непрозрачность компонента. Для практически всех компонентов имеется свойство поворота на выбранное число градусов. Практически это свойство значения не имеет, кроме того, при повороте становятся невнятными свойства позиции и пр.
Для компонентов имеется ряд свойств стиля, например, прозрачность: Opacity, по умолчанию 1.0 — полная прозрачность.
4. ОБЩИЕ СВОЙСТВА КОМПОНЕНТОВ
В Delphi 11 больше свойств для компонентов, чем в Delphi 7, но на практике оказывается, что наиболее важные у наиболее используемых компонентов отсутствуют, что проявляется в неприглядном виде Кнопок, невидимости Панелей и Окошек и пр. Авторы FireMonkey посчитали, что первичное плоское изображение компонентов нужно изменить в соответствии с необходимым дополнительными компонентами, доступными в Effects, в которой располагаются десятки компонентов- свойств.
На деле, практически все компоненты группы Effects предна- значены для специальных эффектов типа мерцания, медленного исчезновения, жалюзи, скручивания и других мало пригодных для прикладных программ эффектов (а для разработки анимации и игр Delphi все еще недостаточно эффективен и медленно работающий). Лишь некоторые из эффектов доступны и эффективны для обычных компонентов: TBevelEffect — эффект фаски (оптимально для Panel) и TInnerGlowEffect — внутреннее свечение, возможно: TGlowEffect — наружное свечение и TShadowEffect. Для задействования эффекта нужно перетащить компонент на форму и через Structure «подчинить» эффект визуальному компоненту управления.
На примере Кнопки видны возможности визуального отображения компонентов: изменение цвета компонента и текста, размера компонента и шрифта, размытие (тень) и добавление иконки (через ImageListEditor, при этом большая и расположенная с краю иконка неприглядна, увеличивает размер кнопки и практически для смартфона мало пригодна).
Другой пример: Окошко (Edit): если в нем нет текста, то компонент практически не виден (как на рисунке 13 сразу под ImageList), поэтому приходится сбрасывать на нее TInnerGlowEffect, который проявляется как уже ясно видимое подчеркивание.
Общие свойства компонентов видны из рисунка выше. Из множества свойств нужно менять лишь некоторые, остальные желательно не трогать.
Для компонентов обычно доступны:
— изменение размеров (и autoSize для Label),
— изменение цвета, в том числе градиентная заливка,
— изменение размера, цвета и типа шрифта (часто центрируют его, изначально расположен с краю — leading).
— видимость и доступность (Visible и Enable = true/false), причем иногда «недоступная» кнопка продолжает работать,
— наиболее важное свойство: Align — выравнивание; в Android на смартфоне важно, чтобы компоненты не «ехали» на разных экранах. Обычно используют несколько панелей вплотную и выравнивание Top и Bottom, а между ними — Client. Часто используется Scale, для разных экранов.
— Также для текстовых компонентов имеется свойство WordWrap (установите как true) для автоматического переноса на следующую строку не умещающегося текста;
— для просмотра не умещающегося на компоненте текста предусмотрено свойство ShowScrollBars и EnabledScroll = true, а также соответствующий компонент для скроллинга, что, впрочем, иногда не работает, а текст видимый на экране не помещается на экране смартфона (поэтому нужно чаще просматривать текущий результат программирования на реальном подключенном устройстве, а не на виртуальных моделях);
— добавление иконки (через ImageList Editor),
— возможность фокусируемости на данный компонент и «Read оnly» — только для чтения,
— стиль (часто приходится изменять установленный для всей формы стиль на подходящий для данного компонента),
— свойства Position (X и Y) от края контейнера (Панели, Формы) и отступы Margin — справ, слева, сверху и снизу,
— всем компонентам доступны вращение и анимации.
Свойство Sender: TObject («Отправитель») присутствует у всех компонентов, хотя редко используется. Но бывает полезно, например, узнать от какого типа источника происходит событие:
if (Sender is TButton) then ShowMassage («Click from Button»);
Или для разного действия при получении события OnClick от разных источников:
if Sender = Button1 then …… else ………..
Или для получения одного результата для разных компонентов: в Events на событие OnClick (или OnChange и т.п.) из выпадающего списка для нескольких компонентов выбрать нужный: например, Button1Click, с выполняемым везде кодом.
Или: Button2.OnClick (Sender); для Button1 — выполнит Button2.
Например, для приложения «Школьный Калькулятор» за счет свойства Sender можно значительно сократить код. Для функции power (степенная функция) в Uses ввести модуль Math.
Введем глобальную переменную S1:String; к которой можно будет иметь доступ из разных участков программы. Сбросим на Форму кнопки-числа и поставим их Text от 0 до 9, а также кнопку-сброс «C» (от Clear) и вспомогательное Edit2.
Для Button1Click:
with (Sender as TButton) do
begin
if Edit1.Text = «0» then Edit1.Text:= Text
else Edit1.Text:= Edit1.Text + Text; // Ввод числа
end;
Для остальных кнопок 2…9 и 0 введем ссылку: в Events на OnClick: Button1Click; // Ввод числа с любой кнопки
Теперь нажатие на любую кнопку-число отправит ее Text (0…9) в Edit1.Text.
Введем кнопки-действия: +, -, *, /, sqr, sqrt и power, sin, cos, Tan, Ln, Log. Для каждой определим сходный код:
S1:=Edit1.Text; // Первое число в глобальную переменную
Edit3.Text:= Button16.Text; // Выводим знак операции
Edit1.Text:= ' '; // Очищаем Edit1 от 1го числа для второго
Button21.OnClick (Sender); // Ссылка на действия с числами
Свойство Sender позволяет перейти на Button2, где имеет место код для всех действий:
//Если операция над 1-м числом — квадратный корень и др.
if Edit3.Text = ’sqrt’ then // Если первым вычисляем корень
begin
Edit2.Text:= «1»;
Edit1.Text:= FloatToStr (sqrt (StrToFloat (s1)));
s1:= Edit1.Text;
EXIT;
end;
//Если 1-е число из 2-х чисел
if Edit2.Text = «1» then
begin
Edit1.Text:= ' '; // Очищаем Edit1 от 1го числа для второго
Edit2.Text:= «2»; // Отмечаем следующее нажатие как 2-е
EXIT;
end
else // Если 2-е число введено
begin
if Edit3.Text = '+' then Edit1.Text:= FloatToStr (StrToFloat (s1) + StrToFloat (Edit1.Text));
************************
Введем также операции с памятью: M+, M-, MR, MC и возможность вводить тригонометрические функции: град или Пи.
В итоге мы имеем простейший калькулятор, который, однако, с высокой точностью производит необходимые операции.
5. Типы программ в DelpHi Android
В Delphi Android существует множество компонентов и еще больше свойств и методов, но далеко не все они нужны в обычном программировании. Можно выделить несколько типов программ в зависимости от задач и сложности.
Прикладные программы.
Прикладные программы как раз и являются основной задачей для Delphi изначально, хотя теснятся сейчас множеством любителей. Между тем, на Delphi можно создать (и создано) достаточно прикладных программ, которые по виду и функционалу близки к Word и Excel.
Для частных прикладных задач для профессионалов можно создавать сложные и функциональные программы минимумом средств как в Delphi 7, так и в Delphi Android. Фактически, для практически любой программы такого типа достаточны:
Label — для надписей,
Edit — для ввода данных,
Button — для запуска операций над ними,
Memo — для вывода результатов.
Для Базы данных таких программ достаточны:
StringGrid — для накопления и отображения данных,
Chart и Sharp — для графиков.
Для работы с файлами достаточно компонента Memo, который имеет сам методы сохранения и загрузки содержимого (в него можно направить данные *.txt для файла или из файла).
Из всего комплекса свойств компонентов необходимыми и достаточными являются:
Text (Line для Memo) — для ввода названий и данных,
Align — для выравнивания на любом экране (выбрать Scale).
Из методов: математические функции для обработки данных,
Перевод число/строка (StrToFloat и FloatToStr);
Операторы цикла и условия для работы с данными:
If… then..else,
For i = n1 to n2 do.
Таким образом, изучив за день буквально десяток компонентов с несколькими методами, можно создавать сложные прикладные программы. Примером этого является, например, созданная нами профессиональная программа «Тест Здоровья», позволяющая достаточно точно определить основные параметры физического здоровья и психической работоспособности.
Как украшения добавлены картинки эмблемы (ImageViewer) и картинки на кнопки (невизуальный компонент загрузки картинок ImageList и свойство ImageIndex для кнопок).
Программы — аналоги программ смартфона.
Это программы, имитирующие Проигрыватель звуковых файлов и Видео, работу с Микрофоном и Bluetooth, Интернет и пр.
Хотя можно добиться (путем достаточно сложного программирования) сходства с функционалом рабочих программ смартфона, однако, это совершенно непродуктивно, так как имеются собственные программы, написанные именно под данный смартфон. К тому же, для работы программ с внешними устройствами самого смартфона требуются разрешения, которые достаточно сложны и не всегда работают и не на всех смартфонах.
Базы данных.
Работам с Базами данных посвящено больше половины компонентов Delphi Android, в том числе специально разработанные класс Fire…, что делает этот раздел специальным направлением программирования. Однако, для Android он практически бесполезен (в отличие от обычного типа Delphi 7), так как круг задач и возможностей для Android узок, а для прикладных программ, как видно из рисунок выше, вполне хватает для создания внутренних Баз данных уже имеющихся компонентов и методов.
Специальные программы.
Существуют специальные возможности работы со звуком, видео, Bluetoth и др., которые используют для создания таких программ, как:
Эквалайзер — улучшение качества звука;
Осциллоскоп — для визуализации звука;
Пульсометр — для работы с видео-сигналом;
Осциллограф — для работы с внешними сигналами; и пр.
Кроме Пульсометра, который может быть создан стандартными средствами Delphi Android (как показано в отдельной главе, посвященной работе со звуком), для таких программ требуются дополнительные библиотеки (Bass. dll), освоение которых часто сложнее обычного Delphi, причем часто бибилотеки не встраиваются в те или иные версии Delphi Android. К тому же, существует достаточно много уже реализованных программ такого типа, обычно написанных на другом языке, так как, например, Пульсометр в Delphi Android 10 и 11 оказывается недостаточно быстр и позволяет получить 5—10 кадров в сек при требованиях для кардиоинтервалографии — 20.
Для работы с внешними сигналами необходимы внешние устройства, часто создаваемые самими, или распространенные микроконтроллеры типа Arduino со своим функционалом, программированием и программами. К тому же, Осциллограф на базе смартфона или компьютера ограничен возможностями его звуковой платы, тогда как существуют дешевые аппараты осциллографы для гораздо более высоких диапазонов, в том числе способные связываться со смартфоном для передачи данных.
6. ТИПЫ ДАННЫХ В DELPHI Android
Типы данных, как обычно, объявляются перед началом самым программы (до begin) используя инициацию var:
var
x,y:Integer;
m: Double;
str1:String; // Строковая переменная.
Mas: array [1..100] of String; // Массив одномерный из 100 строк, начало с 1
Ar: array [0..9] of array [0..9] integer; // Многомерный массив,
D: array of real; // Динамический массив
MyChar: Char; // Тип для хранения простого символа.
R: TRect; //Область, ограниченную R. Left,.Top,.Right,.Bottom;
Point: TPointF;.
Численные значения: Word, Integer, Single, Double, Real; учитывая, что точность составляет 5—6 знаков, обычно используют Integer для целочисленных и Double для чисел с запятой, которые могут быть представлены также в формате Е: 3.14E+2 = 3.14E+00 = 3.14; 23.5Е-2 = 0.235. Для округления числа «x» удобно использовать Round (x), округляющий до ближайшего целого (например, превратить Single в Integer); также можно использовать для выделения целой части Trunc (x) и дробной части Frac (x); Ceil (X) — до целого в большую сторону; Floor (X) — до целого в меньшую сторону;
Для ряда функций нужно добавить пакет math в User начала программы. Доступны многие математические функции, в том числе: абсолютное значение abs (х), квадрат sqr (x) и корень квадратный из х: sqrt (x); для степенной функции преобразование: xn = exp (n*ln (x); корень n-й степени из x = exp (1/n*ln (x), хотя имеется также и функция возведения XY: Power (x,y):
var
Z: Real;
begin
Z:= Power (0.25, 0.5); {Z:= 0.5}
end;
Функция получения вероятного числа: Random (n), по умолчанию пустые скобки () — вероятное число от 0 до 1; при «n» целочисленном — целочисленное значение от 0 до «n-1».
Символы представлены типом Char: Type: Char = #0..#255; Char:= «3». Код символа можно узнать по функции Chr (n). Код ANSI: #0…255, UNICODE: первые 256 символов = ANSI. Chr (66) = B; Char 67) = C.
var
myChar: Char;
begin
myChar:= «G»; // Назначение из символьной константы
ShowMessage («Символ G = ' + myChar); // Видим: «Символ G = G»
myChar:= #65; // Назначение из целочисленной константы
ShowMessage («#65 = ' + myChar); // Получаем «#65 = А»
end;
Присваивание значения требует двоеточия перед равенством: n:= 10; обычное равенство (=) используется в булевых значениях сравнения (true/false): if n = 10 then… Неравенство: X <> Y.
Массивы: важная часть программ для накопления данных.
Могут быть:
— одномерные: Mas: array [1…100] of String; //все начинаются с 1.
— многомерныe: Ar: array [0..9] of array [0..9] of integer;
— динамический массив: D: array of real.
Перед использованием динамического массива устанавливается его длина (начинается с 0): setLeangth (D,20). Закрытие массива: D_M: =nil. элементы массива начинаются с 0, исключая строковой массив, начинающийся с 1.
Доступ к массиву по его индексу: n:= D [21]; соответственно присваивание значения элементу массива: D [21]:=n. Для заполнения массива обычно применяется конструкция цикла:
for n:= 1 to 100 do
begin
D [n]:= n-1; //Нумерует компоненты массива D [100] с 0
end;
Можно найти максимальное и минимальное значение и среднее по массиву: MaxIntVal (D); MinVal (D): double); Mean (D):double), копировать массив в другой с компонента: D:= copy (D, 0, 20) и др.
Строки: ShortString: 255 символов и занимает в памяти
2 байта; String = AnsiString: 1031 символ. AnsiString или WideString содержат большое количество символов. В типе AnsiString символы кодируются в коде ANSI, а в типе WideString в коде Unicode. Общим типом является тип String. String [n] длин строки.
Со строками можно проводить множество операций:
— найти в строке субстроку, ее индекс: n:= Pos (subStr, str);
— копировать строку str c позиции index и числом знаков count: str1:= copy (str, index, count);
— удалить часть строки по индексу delete (str, index, count);
— вставить субстроку: insert (str1, str0, index);
— длину строки можно узнать как: length (str).
Для выравнивания строк полезна функция вставки пустых символов: StringOfChar (»», count); если использовать моноширинный шрифт, то получим идеально выровненные в столбик строчки.
SelStart и SelLength устанавливают позицию 1-го символа и длину записи, SelText — выделенный текст, весь текст выделяет SelectAll; ClearSelection очищает выделенный текст; Clear очищает текст, Undo возвращает предыдущее действие.
Для копирования текста используют CopyToClipBorad, для вставки Paste, для замены символов StringReplace (Строка, Старое значение, новое значение, Флаг [rfReplaceAll, rfIgnoreCase]). Перед копированием целесообразно выделить текст для копирования; Memo.CopyToClipBorad ничего не копирует, для копирования всего содержимого компонента Memo нужно предварительно выделить его содержимое:
Memo.SelectAll; Memo.CopyToClipBorad
Перевод каретки: string1 + #13#10 + string2 (перевод каретки и конец строки с переносом string2 на другую строку).
Изменение типов проводится очень часто, обычно для передачи строкового значения в числовое и наоборот, используют: StrToInt; InToStr; StrToFloat (str); для форматирования: FloatToStrF (str, ffGeneral или ffFixed, count знаков всего, count знаков после запятой. Для Single: x:=Round (x). Для преобразования строки/байты в UTF-8:
bytes:= TEncoding.UTF8.GetBytes (str);
str:= TEncoding.UTF8.GetString (bytes);
TEncoding.ANSI.GetString (Byte);//String из Byte
Строки Java. Для обращения к Java напрямую используют модуль Uses Androidapi. Helpers. Так можно преобразовывать строки:
function JStringToString (const JStr: TJString): String;
function StringToJString (const Str: String): JString;
То же для StrToJCharSequence и StrToJURI.
Дата и Время. Имеется значительное число возможностей работы с датой, но обычно достаточно узнать настоящую дату DateToStr (Now): и текущее время: TimeToStr (Now) и DateTimeToStr (Now).
Форматы представления даты:
dd/mm/yy hh: mm: ss = 09/02/49 01:02:03 mmm = Feb
mmmm = February ddd = Tue
dddd = Tuesday ddddd = 09/02/2049
dddddd = 09 February 2049 hhampm = 01AM
t = 01:02
tt = 01:02:03
dd/mm/yyyy = 09/02/2049
dd/mm/yy hh: mm: ss = 09-02-49 01_02_03 mmm = FEB
mmmm = FEBRUARY ddd = WED
dddd = WEDNESDAY
ddddd = 09-FEB-49
dddddd = WEDNESDAY 09 of FEBRUARY of 1949
hhampm = 01morning t = 01_02_03
tt = 01 _ 02 _ 03. 004
dd/mm/yyyy = 09-02-1949
Для отсчета времени в Delphi 10—12 имеется такой же компонент, как и ранее: Timer, запускающийся при присвоении функции Timer. Enable:= true и выключаемый …false; имеет единственное действие, повторяемое через задаваемый промежуток времени (в мСек). Однако, кроме малой точности (50 мСек) при достаточно нагруженной программе он действует крайне медленно, так что практически часто мало пригоден (хотя вне Android работает вполне точно и хорошо). Для получения времени между 2-мя событиями удобнее использовать функции даты-времени:
var // На Кнопку Start
str, h, m, s: String; t1:Integer;
begin
str:= DateTimeToStr (Now); // Начальное время
h:= Copy (str,, 2);// Отсекаем Дату и сразу берем Часы
m:= Copy (str, 15, 2);
s:= Copy (str, 18, 2);
t1:= StrToInt (h) *3600 + StrToInt (m) *60+ StrToInt (s);
Edit1.Text:= IntToStr (t1); //Текущее время в сек
end;
var // На Кнопку Stop
str, h, m, s: String; t, t1, t2:Integer; begin
str:= DateTimeToStr (Now); // Конечное время h:= Copy (str,12,2);
m:= Copy (str,15,2);
s:= Copy (str,18,2);
t2:= StrToInt (h) *3600 + StrToInt (m) *60+ StrToInt (s); t1:= StrToInt (Edit1.text);
t:= t2 — t1;
Edit3.Text:= IntToStr (t); // Итоговое прошедшее время
end;
Можно также использовать функцию разбора времени:
var
Hour, Min, Sec, Msec: Word;
begin
DecodeTime (Now, Hour, Min, Sec, Msec);
Edit1.Text:= IntToStr (Hour) +IntToStr (Min) +IntToStr (Sec) + IntToStr (Msec);
end;
в мСек:= Hour*60*60*1000 + Min*60*1000 + Sec*1000 + Msec;
TGUID
GUID (Globally Unique Identifier) в Delphi является8-битным идентификатором и используется для уникальной идентификации объектов или компонентов приложения. Обычно представляется в формате шестнадцатеричного числа из 32 цифр, разделенных дефисами. В Windows редактор библиотеки типов автоматически генерирует GUID для новых интерфейсов; также можно сгенерировать GUID, нажав CTRL + SHIFT + G. Пример с GUID:
var
MyGuid1: TGUID;
begin
// Создание нового GUID из строки текста
MyGuid1:= StringToGUID (» {00020400-0000-0000-C000—000000000046}»);
//Отображение в Memo заданного GUID
Memo1.Lines.Add («Заданный GUID: ' + GUIDToString (MyGuid0));
end;
Пример создания Массива (на 2 элемента):
const
MyGuid1 =» {99BDAB12-B1B6—41B0—9BF1—2C1DB3D8EC70}»;
MyGuid2 =» {8C7CD303—8D81—469B-99ED-E1F163E9036F}»;
const
MyArray: array [0..1] of TGUID = (MyGuid1, MyGuid2);
begin
***************
end;
Элемент массива можно создать и запитать им, например, элемент ALRealToAudio из AudioLab, который может получать данные из массива:
x:= StrToInt(Memo2.Lines [i]);
MyArray[0].Create (x);
ALRealToAudio1.InputPins.Pins[0].IsConnectedByIDs (MyArray);
Целый ряд компонентов имеет выход с GUID, что позволяет им взаимодействовать также и с заранее не определенными данными компонентов.
7. ВКЛАДКИ КОМПОНЕНТОВ
Базовые компоненты находятся на вкладке Standard, вклад ка Additional используется значительно меньше, так как ее эле менты практически дублируют вкладку Standart; из вкладки System используют компонент Timer, вкладка Win 32 исчезла.
Компонент Таблица представлен во вкладке Grids двумя таблицами — обычной строковой StringGrid и Grid в котором можно использовать самые разные типы данных и вставлять другие компоненты, также отдельный компонент Заглавие — Header.
Желание разнообразить возможности рисования обернулись их резким усложнением, однако, появилась вкладка Shapes, где представлены различне компоненты: прямоугольник, круг, овал, дуга, линия, подпись и другие типичные элементы рисунков, свойства которых можно задавать программно и рисовать или создавать графики. Компоненты графиков представлены PlotGrid с сеткой и осями координат, на котором фактически нужно рисовать график вручную, а также обычным Chart из TeeChartLite группы.
Вкладку Effects, задуманная для придания красивого вида элементов, использовать можно разве, чтобы сделать хоть как-то видимым компонент Edit. Различные эффекты мигания, скручивания, жалюзи и пр., как и ротация, доступная практически для всех элементов, предназначены для анимации (как и вкладка Animations, вкладка Styles и вкладки для 3D вида компонентов), к которой сам Delphi как раз не предназначен, и работать на смартфоне будет медленно, для прикладных программ это вооб ще дурной тон.
Видимо, вкладка LiveBinding, нововведение авторов языка Firemonkey, отслеживающая «жизнь» компонентов и их связь между собой, также вряд ли подойдет для привыкших к обычному программированию из-за множества специфических функций, которые легко заменить обычными методами Delphi.
Вкладка Color представлена множеством компонентов для работы с цветом, часто взаимодействующими между собой, и также скорее подходят для анимации, чем для прикладных программ. Определенный интерес представляет, возможно, простой ColorBox. Для создания цветового градиента с целью приданию графику вида «зеленая и красная области» подойдут два компонента Rectangle (прямоугольник) из вкладки Shapes, на которые накладываются другие прямоугольники уже как графики данных.
Как уже отмечалось, важными элементами приложения являются компоненты их вкладки Layouts, отвечающие за выравнивание компонентов, в них находящихся, хотя со времен первых вариантов Delphi эту функцию успешно играет компонент Панель — Panel.
Вкладка Common Control представлена рабочими элементами — TabControl и MultiView. Важнейшая вкладка Dialogs в Delphi 10, 11, отсутствует (вернее, не доступна для платформы Android).
Целый ряд вкладок предназначен для работы с Базами данных, в том числе SQL и специальная с широкими возможностями — Fire, вместе с вкладками для работы в Интернет.
Особое значение для Android имеют компоненты работы с элементами смартфона: камерой, сенсорами, аудиоплеером, интернетом, Bluetooth (вкладка Systems), что, впрочем, гораздо лучше делает сам смартфон; нужны специальные разрешения!
Для обычной работы каждого приложения понадобятся:
— из вкладки Standard: Label (метка), Edit (окошко), Memo (многостраничный текстовый компонент, аналог отсутствующего RichEdit), Button (кнопка), CheckBox или RadioButton для выбора различных условий, ListBox (списки), ImageControl вместе с ImageList и Panel для ввода иконок и картинок.
— из вкладки Addinional: PlotGrid для графиков, NumberBox для ввода числовых значений, ComboEdit для сложного ввода;
— из вкладки Grids: обычная StringGrid текстовых данных;
— вкладка Shapes: с уже готовыми фигурами для графиков;
— из вкладки Colors: возможно, ColorBox;
— из вкладки Effects: BevelEffect для создания бордюра компонента Panel, и TinnerGlowEffect для придания видимости ком- поненту Edit;
— компоненты вкладки Layouts нужны для выравнивания содержащихся других компонентов, хотя традиционно эту роль выполняет компонент Panel.
Таким образом, основные рабочие компоненты сосредоточены на вкладке Standart и большинство предназначено для ввода/вывода информации, причем текстового характера.
Типичные компоненты для ввода текста:
— Метка — Label,
— Окошко — Edit,
— компонент многостраничного ввода — Memo,
— Списки — List.
Свойства компонентов можно задавать в Инспекторе объектов или кодом, соответствующим свойствам. При добавлении на Форму компонент появляется в соответствующем Форме стиле, поэтому приходится для каждого компонента убирать все элементы стиля (ставить false во всех свойствах StyledSettings) и создавать свой собственный стиль, нужный именно этому ком- поненту; также можно установить видимый стиль компонента (StyleLookup), обычно его можно пропустить или ставить всегда соответствующим данному компоненты.
Общие свойства (размеры, стиль, положение, видимость, доступность, фокусируемость, позиция и отступы, свойство «только для чтения», свойства шрифта и пр.) описаны выше, также имются некоторые собственные свойства и способы работы с каждым отдельным компонентом.
8. КОМПОНЕНТЫ для ВВОДА И ВЫВОДА ТЕКСТА
8.1. КОМПОНЕНТ МЕТКА — Label
Компонент Label — предназначен для текстовой информации, например, заголовков, но подойдет и для ввода/вывода любого короткого текста. При использовании как заголовка ему лучше придать значение выравнивание Align: Top или Bottom и растянуть на всю Форму, не забыв центрировать текст в TextSetting (изначально свойство представлено как leading).
Общие StyledSetting обычно приходится убирать (false) и ставить собственные в TextSettings, свойства ротации и анимации вряд ли нужны.
Важное свойство: AutoSize — автоматический размер под текст, который можно и не ставить, а лучше сделать побольше размер в высоту и ширину.
Также важно свойство WordWrap = true в TextSettings, которое автоматически переводит не вмещающийся текст на строчку ниже (нумерация строчек в Memo при этом не меняется!).
Свойства компонента можно задавать и кодом. Работа с компонентом состоит обычно в передаче и считывании текста:
Label1.Text:= «Вводимый текст»; //текст в кавычках»»
Label1.Text:= str; // str — строковая переменная
str:= Label1.Text;
Label1.Text. Empty = «»;
Начальный текст обычно задается в Инспекторе объектов, как и его характеристики, которые можно поменять программно в ходе работы приложения.
Из длинного списка свойств и действий, предлагаемого подсказкой после ввода названия компонента и точки, означающей ввод свойств или действий, на самом деле используется обычно только ввод и вывод текста, а чаще всего Label используется вообще однократно при создании приложения для заголовков или подписей.
8.2. Компоненты ввода текста и чисел: Edit, ComboEdit, NumberBox
Edit
Компонент Edit, наверно, наиболее используемый в работе (наряду с Кнопкой — Button, которая и задает действия с ним).
Если в обычном Delphi 7 это обычное белое окошко, то в Delphi 11 это фактически не видимый элемент, с едва заметным подчеркиванием, которое приходится доводить до видимости компонентом из вкладки Effects — InnerGlowEffect.
Если учитывать, что компонент используется для ввода и приходится буквально тыкать в него пальцем на экране, то это становится критичным.
Основным свойством компонента, Edit, передающим введённую информацию, является свойство Text типа String. Вводимый Text можно ввести при создании компонента или затем программно. Не помещающийся текст остается доступен программно, но не виден на экране. Для работы с числами можно перевести текст в число: целое: X:= StrToInt (Edit1.Text); дробное: = StrToFloat (Edit1.Text);
Так как при вводе в компонент используются как текстовые данные цифры, то часто важно установить тип вызываемой при обращении к компоненту клавиатура: KeyboardType — изначальный Default общей клавиатуры заменить на ввод цифр — DecimalNumberPad.
Также можно ограничить максимальное вводимое число знаков (MaxLength) и задать фильтры для ввода символов.
Для ограничения ввода отслеживаются вводимые символы:
Реакция Edit на клавиши: KeyPress:
if key = #13 // клавиша <Enter> then…;
case Key of
«0».. «9», #8:; // цифры и BackSpace
#13: Edit3.SetFocus; // переместить на Enter
«-’: if Length (Edit2.Text) <> 0 then Key:= #0; //ничего не отображать за выходом из рамок окна
else Key:= #0; // остальные символы не отображать.
end;
Можно также:
procedure TForm1.Edit1KeyDown (Sender: TObject; var Key: Word);
var
KeyChar: Char; Shift: TShiftState);
begin
if ((KeyChar <«0») or (KeyChar> «9») or (Length (Edit1.Text)> =11))
and (KEY <> 8) and (KEY <> 46) then //8 и 46 код клавиш Backspace и Del
begin
KeyChar:= #0;
if (Length (Edit1.Text)> =11) = false then
ShowMessage («Допускаются только цифры!»)
else
ShowMessage («Длина текста превысила 11 знаков!»)
end; end;
Или:
procedure TForm1.Edit1KeyPress (Sender: TObject; var Key: Char);
begin
case Key of
«0».. «9», #8:; // цифры и <Backspace>
// Замена точки или запятую десятичной дроби на правильный разделитель — DecimalSeparator
«.»,»,»
begin
Key:= FormatSettings.DecimalSeparator;
//проверка правильности разделителя
if pos (Key, Edit1.Text) <> 0
then Key:= #0;
end;
#13: Edit2.SetFocus; // <Enter> переместиться в поле Edit
else Key:= #0; // Остальные символы запрещены
end;
Проще, однако, установить фильтр ввода данных:
Edit1.FilterChar:= «0123456789»;
Или: keyPress: if key = VK_Eskape (Enter) then…
Или на Edit1KeyPress: if Key=»,» then Key:=».»;//Замена на лету
Ограничить ввод можно свойством ReadOnly, а также свой ствами Enabled и CanFocus; CheckSpelling — контроль правописания.
Задействовав свойство Password (true), можно заменить входящий текст видимыми точками, сделав компонент для ввода пароля.
Выделить часть текста можно:
Edit1.SetFocus; Edit1.SelStart:= 1; Edit1.SelLength:= 1;
С компонентом можно также связать Кнопки, щелкнув по нему правой кнопкой мыши и выбрав в Item Editor, например, кнопку очистки.
ComboEdit представляет собой строку редактирования, комбинированную с кнопкой, аналогично тому, как объединен редактор текста и кнопка выпадающего списка в стандартном элементе ComboBox.
NumberBox.
Для ввода непосредственно чисел не нужно использовать преобразования IntToStr (n), существуют специальные компоненты для ввода чисел (тип Single): NumberBox и SpinBox со свойством Value, при этом для отображения в качестве текста имеется свое свойство:
Label1.Text:= NumberBox1.Text;
Label1.Text:= SpinBox1.Text;
Также следует определить ValueType (Integer/Float) в Object Inspector (однако, при выборе Integer преобразование IntToStr все равно не действует без дополнительного Round). DecimalDigits отображает число десятичных знаков. Свойства HorzIncrement и VertIncrement: шаг приращения. Свойство Value компонента относится к Single, но даже выставив X как Integer в Object Insoector придется изменять тип: y:= round (x).
SpinBox
SpinBox — окно редактирования, может отображать только число, с кнопками для увеличения и уменшения отображаемого значения.
ComboTrackBar
У ComboTrackBar — cвойства те же, и отображение каретки для ComboTrackBar (свойства Visible и CanShow) и режим выпадающего списка.
ComboEdit
Строка редактирования, комбирированная с кнопкой выпадающего списка (DropDownCount), с возможностью ввода строк текста (свойство Items). ComboEdit1.Items.Add (String) добавляет строчку; можно добавить строчку и цифру: ComboEdit1.Items.AddPair (String: String, Value: String).
TdateEdit, TtimeEdit, TCalendar
Позволяют в вид диалога выбрать дату, время. Строка ввода при получении фокуса отображает диалог выбора, результат передается в свойство DateTime (вместе или раздельно).
8.3. КОМПОНЕНТ СПИСОК — ListBox
Компонент ListBox представляет собой Список элементов. Свойства: Count — число элементов списка; Items — массив строк списка; ItemIndex — номер элемента в списке (-1 если ничего не выбрано); text — все содержимое компонента; Sorted — сортировка элементов.
В свойстве items нажав на три точки (…) можно открыть String List Editor и заполнить список (начинается с 0).
Добавить элемент списка можно командой:
ListBox.Items.Add («Вводимый текст»);
причем можно вставить и объект: ListBox.AddObjectsToList ();
Свойство AlternatingRowBakground позволяет окрасить через один.
Свойство columns разделит список на колонки.
Cвойство ShowCheckboxes покажет флажки.
Cвойство ListStyle — горизонтальный или вертикальный;
MultiSelectStyle — разрешает множественный выбор.
Заполнять многостраничный список строками из Memo:
ListBox1.Items:= Memo13.Lines;
Можно каждую строчку, выделив мышью или в Items Editor, форматировать отдельно: тип, размер, цвет шрифта… Или программно:
listbox1.ListItems[n].StyledSettings:= listbox1.ListItems [n].StyledSettings — [TStyledSetting. Family, TStyledSetting.Size];
listbox1.ListItems[n].Font. Family:=«Tahoma’;
listbox1.ListItems[n].Font.Size:= 14;
Главные функции работы с ListBox:
listbox1.ListItems[n].Font. Family:= «Tahoma’;//Шрифт
listbox1.ListItems[n].Font.Size:=;//Размер шрифта
istBox1.ItemHeight; // Высота строки
istBox1.Items. LoadFromFile («Имя_Файла»);//Загрузка файла
ListBox1.Items.SaveToFile («Имя_Файла»); //Сохранить файл
ListBox1.Items.Add («Новая Строка»); //Строка в конец списка
ListBox1.Items.Insert (i, «Новая_Строка»); // Добавление строки перед строкой с номером i
ListBox1.Text — все данные списка;
Индекс строки: i:= ListBox3.ItemIndex;
Получить текст выделенной строки:
str:= ListBox1.Items[ListBox1.ItemIndex];
Доступ к элементу списка:
ListItems [Index] или ItemByIndex (Index);
или with ListBox1 do s:= Items [ItemIndex];
Выделить все: SelectAll; Снять выделение: ClearSelaction;
Поменять местами элементы: Excange [Item1, Item2];
Удаление строки: ListBox1.Items.Delete (i);
Сортировка списка: ListBox1.Sorted:= True;
Поставить в соответствие строкам Списка ячейки Таблицы:
with ListBox1, StringGrid1 do Cells [i, j]:= Items [ItemIndex];
Прокрутить список (выбранная строка вверху):
ListBox1.ScrollToItem(ListBox1.ItemByIndex (n);
Свойство Events: OnClick — основное для работы с компонентом, при этом на первое касание строки она выделяется (серым), на второе — выполняется запрограммированное действие.
Загрузить компонент данными можно и таким образом:
for i:= 0 to ADevices.Count-1 do
begin
Item:= TListBoxItem.Create (ListBox1);
Item.StyleLookup:= ’listboxitembottomdetail’;
Item. ItemData. Text:= ADevices[i].DeviceName;
Item. ItemData.Detail:= ADevices[i].Address;
ListBox1.AddObject (Item);
end;
Здесь также видна возможность редактирования стиля списка:
Item.StyleLookup:= ’listboxitembottomdetail’;
Позволяет загружать список из строки и уменьшенной подстроки:
Item. ItemData. Text:= ADevices[i].DeviceName;
Item. ItemData.Detail:= ADevices[i].Address;
Свойство Bitmap позволяет ассоциировать с элементом картинку.
Полная очистка списка осуществляется методом Clear, обновление:
var
ListBoxGroupHeader: TListBoxGroupHeader;
begin
ListBox1.BeginUpdate;
ListBox1.Clear;
ListBoxGroupHeader:= TListBoxGroupHeader.Create (ListBox1);
ListBoxGroupHeader. Text:=«Files for Sending.»;
ListBox1.AddObject (ListBoxGroupHeader);
ListBox1.EndUpdate;
end;
Можно индивидуально формировать строки списка, в том числе ширину и высоту строк (Общие свойства в Object Inspector — ItemWith и ItemHeight будут отображать нули).
Компонент часто используют для ввода списка, например, файлов:
var
n: Integer;
s, sd: String;
ListBoxGroupHeader: TListBoxGroupHeader;
begin
//Очищаем Memo и ListBox
Memo1.Lines.Clear;
ListBox1.BeginUpdate;
ListBox1.Clear;
ListBoxGroupHeader:= TListBoxGroupHeader.Create (ListBox1);
ListBoxGroupHeader. Text:=«Files List.»;
ListBox1.AddObject (ListBoxGroupHeader);
ListBox1.EndUpdate;
//Ищем и вносим в ListBox1 все *.txt файлы из downlowd
n:=0;
sd:= '/storage/emulated/0/Download’;
for s in TDirectory.GetFiles (sd, «*.txt’) do
begin
n:=n+1;
ListBox1.Items.Add (Copy (s, Length (sd) +2,100));
listbox1.ListItems[n].StyledSettings:= listbox1.ListItems [n]. StyledSettings — [TStyledSetting. Family, TStyledSetting.Size]; //Формат
listbox1.ListItems[n].Font. Family:=«Tahoma’;
listbox1.ListItems[n].Font.Size:= 14;
end;
if n = 0 then
ShowMessage («Нет *.txt файлов в Download!»)
else
ShowMessage («Обнаружено ‘ + IntToStr (n) + «файлов).
Использован вариант перечисления: for s in, а путь к директории объявлен прямо: '/storage/emulated/0/Download’, что возможно при объявлении разрешений на чтение файлов из внешних источников; перед открытием файлов ListBox обновляется, это нужно делать внутри методов:
ListBox1.BeginUpdate;
*****************
ListBox1.EndUpdate;
8.4. КОМПОНЕНТ ComboBox
Компонент ComboBox представляет собой комбинацию Списка и Поля редактирования, что позволяет вводить данные как с клавиатуры, так и по индексу списка, выбрать данные в списке, удалить элемент списка. Он занимает всего одну строчку, в отличие от ListBox.
Count число элементов списка; DropDownCount — количество элементов в раскрытом списке; Text — текст находящийся в поле редактирования (ввода); Items — массив строк списка; ItemIndex — номер элемента в списке (-1 если ничего не выбрано); Sorted — сортировка элементов. В свойстве items можно открыть String List Editor и запол нить список (начинается с 0).
Команда ComboBox1.Items.Add («Вводимый текст») добавит элемент списка; а команда ComboBox1.Items.Clear — очистит список. Вместо стиля текста имеется свойство Style Name.
Аналогично, можно использовать компонент PopupBox:
PopupBox1.Items.Add («Item1»);
Компонент может загружать / сохранять данные в файл.
Компонент StatusBar обычно выровнен по нижней части формы и отображает информацию о приложении по мере его запуска: ProgressBar1.Value:= ProgressBar1.Value + n;
Компонент ToolBar предоставляет пользователю панель инструментов или набор команд, которые можно использовать для активации определенных функций в программном обеспечении. Элементы управления панелью инструментов часто используются в программах для обработки грамматики или текста и обычно предоставляют пользователю быстрый доступ к часто используемым функциям, таким как выделение текста жирным шрифтом, курсивом…
8.5. ListView
FireMonkey введен еще один компонент для просмотра списка, который можно использовать для хранения и представления различных типов элементов — ListView.
Элементы в виде списка могут иметь одну или несколько функций:
Заголовок или текст (используя элемент Item. Text);
Связанное изображение (с помощью Item.Bitmap);
Значок, с помощью Items. Appearance;
Прикрепленная графическая или текстовая кнопка, с помощью Item. ButtonText.
Можно настроить внешний вид представления списка, изменив расположение элементов списка, включая заголовок, связанное изображение, текстовые сведения или значок аксессуара.
В ListView есть режим редактирования, в котором можно выбрать один или несколько элементов.
Элементы в TListView можно добавлять либо путем привязки к источнику данных, либо с помощью кода: ListView.Items.Add для добавления в конец, или внедрить внутрь: ListView.Items.Insert.
Удалить строку: TListView.Items.Delete (i);
Изменить текст в выбранной ячейке на текст в Edit:
ListView1.Items.Item [n]:= Edit1.Text;
Число в строку: i.ToString;
Имеется уточняющее свойство: Detail;
Для заполнения, вставки, сортировки списка используют:
ListView1.BeginUpdate…..EndUpdate.
Свойство OnFilter позволяет тонко обращаться с элементами.
Свойство ItemAppearance (в Onject Inspector) позволяет определить: колонтитулы, внешний вид и поведение компонента:
подсвойство ItemAppearance — пояснительный рисунок, наличие основного и детализированного текста, наличие управляющей кнопки в режиме просмотра;
подсвойство ItemEditAppearance — поведение элемента в режиме редактирования.
В коде программы вместо этих свойств используют: ItemAppearanceName и ItemEditAppearanceName.
8.6. МНОГОСТРОЧНЫЙ ТЕКСТОВОЙ КОМПОНЕНТ Memo
За неимением компонента RichEdit его функции исполняет Memo, имеющая практически те же свойства и действия (кроме форматирования текста); по внешнему виду это простое белое поле.
Нужно установить свой стиль шрифта (в TextSettings, отменив все в StyledSettings); CheckSpelling определяет возможность проверки орфографии; обязательно укажите в настройках WordWrap = true, чтобы не вмещающийся в строчку текст автоматически переходил на строчку вниз. Прокрутка не вмещающегося на экране текста установлена в свойствах автоматически.
Для простого вывода информации нужно установить запрет редактирования: ReadOnly:= true.
Основное свойство компонента: Lines, в которое вводится текст при создании компонента или программно (что является основным):
— текст добавит команда Memo1.Lines.Add («Вводимый текст»);
— получить строчку текста по индексу: str:= memo1.Lines [n];
— скопировать весь текст:
Memo1.SelectAll; Memo1.CopyToClipboard;
Компонент Memo удобен также как база данных. Он может хранить большое количество информации, а заполнить его очень просто, скопировав в Lines в Object inspector при создании компонента строчки, созданные в обычном Word, при этом шрифт сам примет все значения, установленные для Memo.
Можно достаточно просто установить обмен информацией с компонентом Таблица, поставив его Cells [] в соответствие строкам Memo (счет начинается с 0 для обоих компонентов).
Для того, чтобы придать данным из Memо 2-мерный тип, характерный для Таблицы, можно отделить друг от друга части текста в строке, предназначенные для строк (или колонок) Таблицы, например (;), а при загрузки Таблицы использовать функции работы с текстом: копирования части текста до определенного элемента (;), позицию этого элемента, удаление предыдущей части текста для копирования следующей части текста.
Но если в Delphi 7 количество строк в Memo может составлять несколько тысяч, то в ситуации с Android число строк может быть ограничено; при этом можно сохранить и большее число строк, но при обращении Memo1.Lines [101] и более может происходить сбивка адресации и неуправляемые действия.
Для просмотра строчек вне видимости, автоматически предусмотрены установки по скроллингу.
Несомненным достоинством компонента является наличие собственого свойства сохранения и загрузки в файл (без проблем — во внутреннюю область):
Memo1.Lines.SaveToFile (GetHomePath + '/Имя файла. txt’);
Memo1.Lines. LoadFromFile (GetHomePath + '/Имя. txt’);
Другие функции работы с текстом в Memo:
Memo1.ClearSelection; // удалить выделенный текст
Memo1.CutToClipboard; // вырезать в буфер обмена
Memo1.CopyToClipboard; // скопировать в буфер обмена
Memo1.PasteFromClipboard; //вставить из буфера обмена
Memo1.SelText; //функция возвращает текстовую строку выделенного текста
Memo1.Undo; // отмена последнего изменения
Memo1.Lines.Delete (0); //удаление 0 строки
Memo1.SelStart:= 0; //начальное положение выделения
Memo1.SelLength:= 15; //Длина выделенного текста
Если необходимо всегда видеть выделенный текст, даже при переходе управления на другой элемент управления, то установите свойство HideSelection в false.
8.7. КОМПОНЕНТЫ ВЫБОРА CheckBbox и RadioButton
Компоненты CkeckBox и RadioButton используются для выбора уже известных условий.
Компоненты RadioButton обычно объединяются в группы внутри компонента-контейнера GroupBox; активна одна кнопка, для определенной группы, что достигается простым помещением группы связываемых кнопок в контейнер GroupBox, а также приданием одинакового имени (например «1») свойству GroupName.
Как сам контейнер, так и кнопки, имеют свойство Text, что может использоваться для показа условий.
Выбрать компонент можно функцией:
RadioButton1.isChecked:= true;
Работа с компонентом производится на свойства в Events: OnChange и OnClick, а также как выбор в коде:
if RadioButton1.isChecked = true then ….
Также удобно изменять текст при компоненте:
if RadioButton1.isChecked = true then RadioButton1.Text:= «Выбор сделан»
else RadioButton1.Text:= «Отказ от предложения».
Надо отметить, что компонент прихотлив, и довольно часто неизвестно что от него ждать; если он явно не виден в настоящий момент, то его состояние (isChecked) может и не считываться, в таком случае следует поставить ему в самом начале, когда компонент виден, в соответствие компонент Edit, который доступен в любом состоянии и в любом месте:
if RadioButton1.isChecked = true then Edit1.Text:= «1»; // В начале при выборе RadioButton
if Edit1.Text =- «1» then …. // в любом месте программы
CheckBox.
Флажок (независимый переключатель) отличается от выше описанного переключателя тем, что в группе флажков одновременно можно установить флажки в любой комбинации (в том числе могут быть установлены или сброшены все флажки).
Флажок может находиться в установленном или сброшенном состоянии. Одиночный флажок часто используется, например, для включения / выключения какого-либо режима. Флажок выглядит как прямоугольник с текстовым заголовком. Если в нем есть галочка, то обозначенная этим флажком опция включена (в этом случае также говорят, что флажок отмечен). Если прямоугольник пуст, то флажок снят, или сброшен. Дейстия с одним флажком не отражаются на состоянии других флажков, если это не было специально предусмотрено.
В Events используют свойства OnClick и OnChange, но обычно просто в программе используются состояния группы флажков и в зависимости от этого выполняются действия.
9. КОМПОНЕНТЫ ДЛЯ РАБОТЫ С ДАННЫМИ — Таблицы и Базы
Собственно, все компоненты текстового ввода/вывода рабо тают с данными, но традиционно именно Таблица предназначена для их накопления и отображения. Создать таблицу можно поместив ее на Форму и назначив число колонок через Items Editor, при этом могут быть назначены разные виды колонок для ввода разного типа данных (на практике ограничиваются обычной строковой колонкой и при необходимости преобразовани ем типов вводимых данных).
В Delphi 10.3, 11, имеется как старая текстовая таблица StringGrid, так и новый введенный компонент Grid. Если работа с StringGrid такая же, как и ранее, то компонент Grid фактически представлен набором колонок, каждая из которых отображается и программируется отдельно. Чтобы добавить колонку программно теперь придется приводить новую конструкцию создания новой колонки с указанием ее ширины:
StringGrid1.AddObject(TStringColumn.Create (nil));
StringGrid1.Columns [i].Width:= 35;
Соответственно, удалить колонку можно оператором Release (после чего придется перенумеровывать колонки):
StringGrid1.Columns[i].Release;
Основные свойства таблицы остались прежние и отображены в Инспекторе объектов в свойствах Option.
Если с таблицей не предполагается работать визуально (изменять размеры строк, колонок и пр.) достаточно оставить отображение линий колонок.
Также можно убрать нововведенный элемент Заголовок — Header, который представляет собой отдельный элемент и программируется отдельно, а фактически выполняет роль окрашиваемых серым нулевых колонку и строку, предназначенных для их нумерации в более ранних версиях таблицы.
Так как нулевые колонки никуда не делись, а важность нумерации строк остается, лучше убрать этот только мешающий элемент и оставить за нулевыми колонкой и строкой функции их нумерации или обозначения; при этом становится более понятным нумерация данных, не приходится помнить, что данные 1 находятся в строке 0 и при обращении к таблице постоянно изменять: n:= n-1.
Основным способом работы с таблицей остается работа с клетками [Cells] с указанием координат — колонки Columns (с) и строки Row (r): StringGrid1.Cells [c,r], с операторами присвоения:
StringGrid1.Cells [c,r]:= «текст» или переменной str: String;
str:= StringGrid1.Cells [c,r];
Так как часто содержание Таблицы — числа, то обычным является применение изменения типов данных: StrToInt (), IntToStr (), StrToFloat (), часто с одновременным форматированием: FloatToStrF (n, ffFixed, count всего цифр, count цифр после запятой).
Данным в таблице можно поставить в соответствие данные Memo (нумерация начинается также с 0 и также первую 0-строчку можно для ясности пропустить). Так же, как и в Memo число строк в таблице может быть ограничено, что прямо указывается в Инспекторе объектов в свойстве RowCount; однако, в таблице присутствуют еще и колонки, чего нет в Memo. Поэтому при обращении к Memo и обратно приходится переходить от 1-мерности Memo к 2-мерности таблиц, записывая в Memo все подряд, а при заполнении таблицы разбивая сплошной текст на участки соответствующие строкам и колонкам.
В Memo строки могут быть большой длины (автоматический перевод на другую видимую строку WardWrap:= true не отражается на нумерации строк).
Поэтому все строки таблицы в пределах одной колонки можно совместить, вводя разделитель (например «;») и записать длинную строку.
Обратный переход потребует несколько большего кода, включающего работы со строками для выделения фрагментов строки Memo между «;» соответствующего строке таблицы.
Большие таблицы можно визуально отражать с определенной строки — TopRow и колонки — LeftRow. Часто таблица предназначено только для чтения — ReadOnly или вообще для промежуточных действий и невидима — Visible:= false.
Новый тип таблиц — Grid позволяет вводить различный типы колонок, например, с цифровым вводом, избавляясь таким образом от необходимости переопределять тип вводимых данных. При этом основным элементов таблицы является колонка, а сама таблица представляет собой по существу, набор колонок разного типа. Ширину колонок можно изменять программно или визуально мышью, а также выделив колонку определить ее ширину в Инспекторе объектов или прямо двигая границу колонки мышью. Ширину строки определяет свойство в Инспекторе объектов.
В соответствии с основной идеей Firemonkey, согласно которой каждый компонент — это также и контейнер для других элементов, TGrid представляет собой контейнер в котором можно разместить FMX-объекты как коллекции строк:
TColumn: столбец таблицы с неопределенным содержимым. Можно использовать для создания своих столбцов (с ComboBox и другими элементами);
TStringColumn: столбец содержит только строки;
TCheckColumn: в столбце будут размещаться чекбоксы;
TProgressColumn: для отображения прогресса операции;
TPopupColumn: столбец со списком выбора;
TGlyphColumn: столбец с картинками;
TDate.. и Ttime.. — специально для даты и времени;
Tinteger… Currency… Float — для различных типов чисел.
Для смешанной таблицы работают обычные для StringGrid процедуры, так, для строки «3» четырех разнотипных колонок:
StringGrid1.Cells [0,3]:= «Это строка 3»; //Колонка для строк
StringGrid1.Cells [1,3]:= ’true’; //Колонка для CkeckBox
StringGrid1.Cells [2,3]:= ’21:20»; //Колонка для ввода времени
StringGrid1.Cells [3,3]:= «123»; //Колонка для чисел
StringGrid1.Cells [4,3]:= «2»; // Колонка № Glyph из ImageList
Базы данных — одно из востребованных возможностей для программ на персональных компьютерах, на приложениях для Android используются реже, однако, разработана специальная группа компонентов Fire, также часто используется база SQLite, встроенной в приложение (группа FireDoc).
Компонент FDConnection обеспечивает соединение с базой данных, компонент FDTable — доступ к таблицам, компонент FDQuery — связь с базой данных посредством SQL запросов. Отображение данных обычно идет для ключевых элементов в Списках, а основная информация — в Таблицах.
Компоненты для Баз данных занимают большую часть палитры Palette, хотя многие не доступны для платформы Android; работа с ними часто идет вместе с компонентами для работы с Интернет и гораздо лучше проводить их на персональном компьютере.
Также, для баз данных и WEB-приложения гораздо лучше подходит язык Phyton с библиотеками.
10. ГЛАВНЫЙ КОМПОНЕНТ ДЛЯ ПРОГРАММИРОВАНИЯ — Button
Для активации какого-либо действия предназначен основной компонент Кнопка — Button, хотя практически у каждого компонента имеется обильный список свойств и возможностей, среди которых важнейшие: onClick, onChange и реакция на получение фокуса, вход в компонент и выход из него.
На примере Кнопки видны возможности разного визуального отображения компонентов: изменение цвета компонента и текста, размера компонента и шрифта, размытие (тень) и добавление иконки (через ImageListEditor), при этом размер иконки меняется с уменьшением кнопки (с увеличением — до размера самой иконки).
Добавление иконки (начальное значение ImageIndex:= -1 как отсутствие иконки) с помощью ImafeList.
Все эти художества, однако, не спасают компонент Кнопку и достичь обычного для Delphi вида не удается; она предстает как бледный полупрозначный плоский прямоугольник. Можно окрасить кнопку (свойство TintColor) и текст на ней, но это не намного улучшает вид кнопки, как и применение элементов из группы Effects.
Другие типы Кнопки из других групп также не лучше. Получить красивую Кнопку можно из компонента Панель, задействовав TBevelEffect для получения объемного вида, но другие эффекты не воспроизводятся, как и не меняется цвет панели; к тому же компонент Панель утратила текст на ней и цвет и для получения названия новоявленной Кнопки нужно сбросить на нее компонент Label. Кроме того, такая многоэтажная конструкция не только утяжеляет программу и сложность, но и непредсказуемо перестает работать).
Несколько лучше, чем в Delphi 10, ведут себя кнопки в Delphi 11, Android.
11. КОМПОНЕНТЫ ДЛЯ 2D И 3D ГРАФИКИ
11.1. КОМПОНЕНТЫ ДЛЯ ГРАФИКИ: ImageViewer, ImageControl, Image, PaintBox, Chart, Shape
Свойство Images есть у ряда компонентов, используется обычно у компонента Кнопка (Button) для ввода иконки — пояс нения действия кнопки. Используется вместе с компонентом ImageList (см. Рис. 13), в который предварительно загружаются картинки — иконки, которые и используются по ImageIndex (раскрывается вся серия загрузок, из которых и выбирается простым указанием курсора). Изменение размера кнопки уменьшает иконку, увеличение — только до истинного размера иконки.
В Delphi 10, 11, имеется также компонент ImageControl (вместо обычного Image), который загружается уже через свойство Инспектора объектов Bitmap/Bitmap Editor. Простой рисунок используется, например, для вывода эмблемы в названии приложения и т. п.
Однако, Image Control имеет гораздо большие возможности и используется активно в ходе программирования для вывода различных картинок и для рисования на компоненте, для чего компонент имеет свойства загрузки из файла — LoadFromFile, диалога — ShowOpenDialog; для рисования Bitmap, Canvas и др. Изменение размеров компонента изменяет и размер выводимого изображения (качество изображения зависит от его размера, который может быть выше для компонента, чем для рисунка); при этом сам рисунок изменяется пропорционально.
Компонент ImageViewer также загружается через Bitmap и имеет свойство масштабирования BitmapScale, которое к тому же анимировано и позволяет изменять масштаб вывода картинки в ходе работы приложения. Также имеется свойство ShowScrollBars для невмещающейся части картинки (Обычно лучше отключить = false).
11.2. Группа компонентов Shape
Все фигуры можно взять готовыми из группы Shapes, что значительно упрощает работу с ними через готовые свойства и настройки. В Delphi Android, однако, компонент TPath, удобный для линейного графика, конфликтует с TPath классом, ответственным за работу с файлами, и System.IOUtils в Use.
Прямоугольник Rectagle подойдет для отображения столбчатых графиков. Компонент можно сделать цветным и окрасить градиентом, например, в красный или зеленый цвет, показывая границы нормы.
Для линейного графика можно использовать компонент Path: TPath, рисующий полилинию, а также компонент PlotGrid, представляющий собой просто сетку. Чтобы координата Y шла вверх (а отсчет Y ведется от левого верхнего угла вниз), и находилась в пределах PlotGrid, можно координату Y задавать как:
y1:= Form1.Height — (Form1.Height — PlotGrid1.Height) — y;
var
p: TPointF;
i: Integer;
x,y,kX, kY, x1,x2,y1,y2:Double;
s: String;
begin
//Коэффициенты Х и Y
kX:= StrToFloat (Edit1.Text);
kY:= StrToFloat (Edit2.Text);
//Оси задаются
x:= 1; // Ось Y
y:= 1;
p.X:= x;
p.Y:= y;
Path1.Data.MoveTo (p);
x:= 1;
y:= 360;
p.X:= x;
p.Y:= y;
Path1.Data.LineTo (p);
x:= 1; // Ось Х
y:= 360;
p.X:= x;
p.Y:= y;
Path1.Data.MoveTo (p);
x:= 360;
y:= 360;
p.X:= x;
p.Y:= y;
Path1.Data.LineTo (p);
//Проверка наличия данных
if Memo1.Lines [0] = «» then
begin
ShowMessage («Введете данные X;Y!»);
Exit;
end;
//График
s:= Memo1.Lines [0];
s:= Copy (s,1,Pos (» -», s) -1); // Выделение Х из строки
x:= StrToFloat (s);
s:= Memo1.Lines [0]; // Выделение Y из строки
s:= Copy (s, Pos (» -», s) +1,100);
y:= StrToFloat (s);
x1:= kX*x; // Учет коэффициентов для данных
y1:= Form1.Height — (Form1.Height — PlotGrid1.Height) — kY*y;
p.X:= x1;
p.Y:= y1;
Path1.Data.MoveTo (p); //Начало графика
//График
for i:= 1 to Memo1.Lines.Count-1 do
begin
s:= Memo1.Lines [i];
s:= Copy (s,1,Pos (» -», s) -1);
x:= StrToFloat (s);
s:= Memo1.Lines [i];
s:= Copy (s, Pos (» -», s) +1,100);
y:= StrToFloat (s);
x1:= kX*x;
y1:= Form1.Height — (Form1.Height — PlotGrid1.Height) — kY*y;
p.X:= x1;
p.Y:= y1;
Path1.Data.LineTo (p); // Очередная линия графика
end;
Надо заметить, что компонент рисует график сразу, причем самостоятельно масштабирует его во всю свою длину и ширину, так что можно просто наложить по размеру компонент на PlotGrid. Повторные графики рисуются с сохранением предыдущих, причем большие графики изменяют масштаб предыдущих, а меньшие рисуются в пределах существующего.
Ранее заданные оси координат (синия линии по Х оси) автоматически увеличены под новый график.
«Стереть» линии можно просто с помощью Path1.Data.Clear.
Чтобы график не выходил за заданные масштабы и компонент не навязывал свой масштаб, данные для графика должны быть в пределах координатных заданных осей X и Y. Для того, чтобы задать масштаб компоненту нужно нарисовать вначале линии координат. Вводить данные можно из Memo.
Для масштабирования графика можно задать вводимый масштаб для данных через компоненты Edit:
kX:= StrToFloat (Edit1.Text);
kY:= StrToFloat (Edit2.Text);
Компонент Path можно использовать и для вывода данных в реальном времени, когда работа с данными периодически дополняет график, для этого достаточно добавлять данные в Memo и использовать его свойство onChage для перерисовки графика (очищаем график с помощью Path1.Data.Clear и рисуем заново с новыми данными).
Из простого графика можно получить полноценный прибор — регистратор сигнала. Регстратор позволяет задавать время дискретизации, коэффициент усиления сигнала, отображать текущие данные и их счет, листинг данных в Memo, копировать данные в буфер. Выбросы за пределы экрана не отображаются (прерывистый график). Дополнительный коэффициент позволяет преобразовать данные в единицы измерения (в нашем случае в температуру). При этом код получается очень компактным:
var
p: TPointF;
i, rnd: Integer;
n: Single;
kY, x1,x2,y,y1:Double;
s: String;
begin
if Edit1.Text = «1» then //Случайные данные для проверки!
begin //На ширину Path = 119 данных по 3 точки
//Начальную точку устанавливаем к началу координат.
if NumberBox6.Value = 0 then
begin
NumberBox6.Value:= 1;
p.X:= 1;
p.Y:= Path1.Height -1;
Path1.Data.MoveTo (p);
end;
rnd:= Random (100) *5+10; //Случайные данные
NumberBox2.Value:= rnd;//Истинное значение выводится
kY:= StrToFloat (Edit2.Text); //Коэффициент Y
NumberBox5.Value:= NumberBox5.Value+1;//Счет данных
NumberBox6.Value:= NumberBox6.Value+1;//Счет для цикла
s:= FloatToStr(NumberBox4.Value*NumberBox5.Value) + '; ' + FloatToStr(NumberBox2.Value); //В Memo истинные данные
Memo1.Lines.Add (s);
//Рисуем график не выходя за пределы Path
x1:= NumberBox6.Value*3;// 3 Точки на 1 значение Х
y:= rnd*kY;
if y <= Path1.Height — 5 then //Коррекция Y
begin
y1:= Path1.Height — kY*y;
p.X:= x1;
p.Y:= y1;
if NumberBox6.Value> = 119 then //За пределы Х
begin
Path1.Data.Clear;
NumberBox6.Value:= 0;//Новый цикл! С начало
end
else
Path1.Data.LineTo (p) //Рисуем график — линию
end
else // Y За пределы графика
begin
p.X:= x1;
p.Y:= 2;
Path1.Data.MoveTo (p);//Прерывание графика
end; end; end;
На рисунке представлен вид такого регистратора (в режиме теста — регистрация случайных данных — Random (100) +1) *2).
Другие фигуры также могут быть использованы для графика: эллипс (как вариант — круг) может быть использована для точечного графика (как и просто точка) с применением Rect координат:
R.Top:= Path1.Height — Y; // Координата Y, отсчет сверху
R.Bottom:= R.Top+2;
R. Left:= X; // Координата Х (в пределах Path. Width)
R. Right:=R. Left+2;
Path1.Data.AddEllipse (R); //Добавление очередного объекта
Аналогично, TPie может быть использован для кругового графика (заполненная цветом — как сектор).
Для надписей также есть своя фигура TText.
К сожалению Path: TPath графика конфликтует с Path: TPath пути для файлов! Поэтому одновременно рисовать и сохранять данные не получится; придется использовать рисование: Path: TPathData.
11.3. Компоненты типа Image и Paint
Для собственно рисования имеются компоненты PaintBox и Image (обычно для иллюстраций) из группы Shapes, имеющие свойства для рисования: Bitmap, Canvas, Begin Scene и End Scene (внутри которых происходит подготовка к рисованию), для загрузки рисунков — загрузка из файла и из потока: LoadFromFile (директория, имя файлы,) LoadFromStreem (…); сохранить и распечатать рисунок:
Chart1.SaveToBitmapFile('c:\Папка\btmp.jpg’);
ShellExecute (0, ’print’, 'c:\Папка\btmp.jpg’, nil, nil, 0);
В свойстве WrapMode: Stretch: отображать целиком с искажениями по размеру компонента; Center: отображается только центральная часть иллюстрации; Fit: масштабирование без искажений; Original: отображается левая верхняя граница в пределах компонента; Tile: если размер картинки меньше, то выводятся «плитки» картинки.
Вывод подготовленных ранее рисунков осуществляется с помщью PaintBox1.PaintTo () с указанием Canvas и области выведения: константы Rect; PaintBox1.Canvas. DrawBitmap () с указанием Bitmap и областей Rect начального и конечного переноса.
Методы рисования позволяют нарисовать элементарные фигуры например, PaintBox1.Canvas.FillRect () — закрашенный прямоугольник с закругленными краями, и др. Доступ к Image, являющимся объектом TCanvas, осуществляется через свойство Bitmap.
В общем виде вывод рисунка проводится как:
Объект.Canvas. Метод (параметры): PaintBox1.Canvas.FillRect ().
Свойство Canvas имеет возможности нарисовать различные фигуры (draw) — линию, прямоугольник, эллипс, и закрасить их (Fill). Цвет можно определить через TAlphaColor. Blue (и др. цвета выводимые подсказкой). Толщину и вид линии определяют свойтва Stroke, Thickness и StokeDash, а стиль закраски — FillKind.
Важно: для вывода рисунка его надо оформить внутри Begin Scene и End Scene. Координаты задаются для линии точками X и Y начала и конца линии, объекта TPoint; для других фигур — объектом TRect, описывающий область. Код для рисования линии на компоненте Image1 при открытии приложения:
procedure TForm1.FormPaint (Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
var
p1,p2:TPointF;
begin
p1.X:=Image1.Position. X;
p1.Y:=Image1.Position. Y;
p2.X:=Image1.Position. X+100;
p2.Y:=Image1.Position. Y+150;
if Canvas.BeginScene =true then
try
Canvas.Stroke.Kind:=TBrushKind.Solid;
Canvas. DrawLine (p1,p2,10);
finally
Canvas. EndScene;
end; end;
var
p1,p2:TPoint;
begin // Рисует линию
p1.X:= 40;
p1.Y:= 40;
p2.X:= 100;
p2.Y:= 100;
Image1.Canvas.BeginScene;
Image1.Canvas. DrawLine (p1,p2,1.0);
Image1.Canvas. EndScene;
end;
//Рисует квадрат
Var
R: Trect;
begin
R. Left:= 30;
R.Top:= 30;
R. Right:= 100;
R.Bottom:= 100; // Y1
Image1.Canvas.BeginScene; //создание графической сцены
Canvas.Fill.Kind:= TBrushKind. bkSolid; // Тип линии
Canvas.Fill.Color:= TAlphaColors. Blue; // Цвет закраски области
Image1.Canvas.DrawRect(r,5,5,[TCorner.TopLeft,
TCorner.BottomLeft],0.8);
Image1.Canvas. EndScene; //завершение сцены и вывод изображения на экран
end;
DrawRect(r,5,5,[TCorner.TopLeft, TCorner.BottomLeft],0.8) — включает указания на область вывода прямоугольника (r), радиусы закругления углов (5,5) с указанием какие именно углы следует закруглить [TCorner.TopLeft, TCorner.BottomLeft] и указать степень прозрачности фигуры (0.8).
Для прорисовки на поверхности холста графического образа следует создать Bitmap: метод DrawBitmap (const ABitmap: TBitmap; const SrcRect, DstRect: TRectF; const AOpacity: Single; const HighSpeed: Boolean = False):
var
ABitmap: TBitmap;
aWidth, aHeight: integer;
SrcRect, DstRect: TRectF;
begin
with Form1.Canvas do
begin
ABitmap:= TBitmap.Create;
aWidth:= ABitmap. Width;
aHeight:= ABitmap. Height;
SrcRect:= RectF (0,0,aWidth, aHeight);
DstRect:= SrcRect;
if BeginScene then
begin
DrawBitmap (ABitmap, SrcRect, DstRect,1,true);
EndScene;
end;
end; end;
Изображение ABitmap полностью или его части представлено SrcRect, место вывода и размеры — в параметре DstRect.
Параметр HighSpeed позволяет предпочтение скорости (true) или качеству (false) вывода.
Фрагмент исходного рисунка, можно ограничить в SrcRect. Управляя размерами области DstRect, можем увеличить или уменьшить рисунок.
Метод Paint: PaintTo (const ACanvas: TCanvas; const ARect: TRectF; const AParent: TFmxObject = nil); пример: рисуем увеличенную в 2 раза кнопку:
Form1.Canvas.BeginScene;
Button1.PaintTo(Form1.Canvas, RectF (0,0,Button1.Width* 2,Button1. Height*2));
Form1.Canvas. EndScene;
Надо отметить, что если в событии OnPaint Формы все рисуется прекрасно, то при попытке сделать это же при нажатии кнопки можно получить что угодно, а обычно ничего, или кратковременный вывод рисунка на экран, который тут же исчезает. Чтобы получить устойчивую фигуру нужно сделать довольно много, в отличие от обычного Delphi 7. На примере компонента Path, позволяющего рисовать полилинии:
Вначале определяется глобальная переменная Path:
var
Form1: TForm1;
Path: TPathData;
Затем при открытии формы создается траектория Path:
procedure TForm1.FormCreate (Sender: TObject);
begin
Path:= TPathData.Create; //Создаем траекторию
end;
Определяется событие OnPaint для Image (или для Form1):
procedure TForm1.Image1Paint (Sender: TObject; Canvas: TCanvas; const ARect: TRectF);
begin
with Canvas do
if (Path.Count> 0) and (BeginScene) then
try
Clear (TAlphaColorRec. White);
Stroke.Kind:= TBrushKind.Solid;
Stroke.Thickness:= 1;
DrawPath (Path,1); //Рисовать Path при обращении
finally
EndScene;
end;
Собственно рисуем линию на Image по щелчку на кнопке:
procedure TForm1.Button1Click (Sender: TObject);
var
p,p1:TPointF;
begin
p.X:= Image1.Position. X;
p.Y:= Image1.Position. Y;
Path.Clear; // Очистить Path
Path.MoveTo (p1); // Начальная точка рисования
p1.X:= Image1.Position. X+100;
p1.Y:= Image1.Position. Y+150;
Path.LineTo (p1); // Линия от начальной точки
Path.ClosePath; // Оконачание создания Path
// Вывод созданной Path в Image
Image1.OnPaint(Sender,Image1.Canvas,Image1.ClipRect);
end;
Для добавления элементов используют Path1.Data.Add…
(или: Image1.AddObject (TEllipse)):
for i:= 0 to Memo1.Lines.Count — 1 do
begin
X:=X+5; // X с шагом = 5 точек
R.Top:=Path1.Height — StrToInt(Memo1.Lines [i]) // Y
R.Bottom:=R.Top +3; // Диаметр кружка: 1+3=4
R. Left:= X;
R. Right:=R. Left +3;
Path1.Data.AddEllipse (R); //Рисуем кружок
end;
Как видите, рисование в Android не доставляет того же удовольствия, как в Delphi 7. Поэтому гораздо проще использовать фигуры из группы Shape, которые не требует ничего этого и свойства которых можно определять в Object Inspector.
11.4. РИСОВАНИЕ СРЕДСТВАМИ DELPHI ANDROID
Основной класс для для графики в Delphi Android — TCanvas (т.н. «холст») модуля FMX.Graphics. В отличие от обычного Delphi рисование средствами Delphi Android, в результате попыток создания мультиплатформенности, оказывается резко усложнено и вначале может вообще ничего не получиться с выводом рисунка на экран, а в особенности с динамическим выводом данных, хотя для последнего существует специальный компонент для вывода графиков TChart.
Основу рисования составляет Canvas: TCanvas, который внедрен во многие компоненты и обеспечивает рисование на них. Вывод графики лучше осуществлять в рамках события OnPaint, присутствующего у многих компонентов.
Для оценки цвета можно использовать канал RBG (Red, Blue, Green), максимальное значение 255, у яркости максимальное 240. При переводе в градации серого используется:
Яркость = 0.6*Зеленый +0.3*Красный +0.1*Синий. Коэффициенты отражают неравномерность чувствительности глаза к разным участкам спектра.
В Uses следует добавить: System. UITypes.
Создать цвет, например, для компонента прямоугольник, можно смешав 3 цвета (A: Byte = $FF — прозрачность до 255):
var
col: TAlphaColorRec;
begin
col. R:= 255;
col. G:= 255;
col. B:= 255;
col. A:= 255;
Rectangle1.Fill.Color:= TAlphaColor (col);
Rectangle1.Fill.Kind:= TBrushKind.Solid;
end;
Для назначения цвета можно использовать вывод его в 16-ричном формате: $FFF000000 соответствует непрозрачному красному цвету; но удобнее и чаще всего используют TAlphaColor, который использует и 16-речные значения и стандартные цвета:
var color: TAlphaColors;
color:= TAlphaColors.Red;//Подсказка выводится после точки
Предусмотрены также ряд специализированных компонентов на вкладке Color: TColorPanel, TComboBoxColorPanel, TColotListBox, TColorComboBox, TColorPicker и др.
Рисование фигур включает прежде всего описание их свойств внутри BeginScene….EndScene. Так, единственный метод вывода линии ранее:
DrawLine (P1, P2, 1);
теперь превращается в целую историю:
в событии OnPaint основной Form1 подготавливаем сцену:
constant R: TREctF;
var
P1, P2: TPointF;
x1, y1,x2, y2: Integer;
color: TAlphaColor;
Begin
x1:= 10;
y1:= 20;
x2:= 100;
y2:= 200;
P1.X:= x1;
P1.Y:= y1;
P2.X:= x2;
P2.Y:= y2;
with Canvas do
if (Edit1.Text = «1») and (BeginScene) then
try
Stroke.Kind:=TBrushKind.Solid;
Stroke.Color:=TAlphaColors. Blue;
Stroke.Thickness:=2;
Canvas. DrawLine (P1, P2, 1);
finally
endScene;
end;
Для вывода рисунка здесь нужно нажать Button и присвоить Edit1.Text значение «1». При не ограничении вывода рисунок появится прямо при открытии Приложения.
Методы Canvas:
Filll — используемая кисть, имеет множество возможностей;
Stroke — кисть-перо для рисования линий;
Font — для шрифта (Name, Size, Color).
Пример для вывода шрифта:
FillText (RectF (20,30, 200,300), «Текст вывода», true, 1, [], TTextAlign. Center);
Заливка фигур осуществляется методом Fill… для прямоугольника (Rect), эллипса (Ellpise), дуги (Arc), полигональной линии (Polygon) и тректории (Path); полные свойства выводятся автоматически при написании в программе.
При этом Path: TPathData уже не конфликтует с путем для файлов Path: TPath, как это имеет место для Path: TPath группы Shape, однако, рисовать Path: TPathData гораздо менее удобно, чем работать с уже готовыми фигурами Path: TPath.
Для Path: TPathData работают известные методы — перемещение в точку:
MoveTo (const P: TPointF);
и проведение линии от предыдущей точки:
LineTo (const P: TPointF);
Для доступа из любого места программы нужно создать глобальную переменную:
var
Form1: TForm1;
Path: TPathData;
При открытии на OnCreate в Form1 создаем траекторию:
Path:=TpathData.Create;
Подготавливаем сцену для визуализаци в OnPaint и Form1:
with Canvas do
if (Path.Count> 0) and (BeginScene) then
try
Stroke.Kind:=TBrushKind.Solid;
Stroke.Color:=TAlphaColors. Blue;
Stroke.Thickness:=2;
DrawPath (Path,1);
finally
endScene;
end;
Для рисования полилинии на экране, например при движении пальцем, используем метод OnTouch в Form1:
OnTouch в Form1:
case Action of
TTouchAction.Down: begin
Path.MoveTo (Touches [0].Location);
end;
TTouchAction.Move: begin
Path.LineTo (Touches [High (Touches)].Location);
FormPaint(Sender,Form1.Canvas,Form1.ClientRect);
end;
Очистить холст можно методами:
Canvas.Clear (const AColor: TAlphaColor = 0)
или только область:
Canvas.Clear (const ARectF; const AColor: TAlphaColor = 0).
Для прорисовки на поверхности холста графической фигуры существует метод DrawBitmap (константы: ABitmap: TBitmap; SrcRect и Dstrect: TRectF: AOpasity: Single; HighSpeed: Boolean = false): выводится все изображение или его часть (SrcRect в область DstRect). Загружать образ фигуры можно из файла:
var ABitmap:= TBitmap.CreateFromFile («Путь и Имя файла»);
для вывода рисунка:
if BeginScene then
begin
DrawBitmap (ABitmap,1,true);
EndScene;
end;
Сохранить настройки холста можно функцией:
SaveState: TCanvasSaveState;
Восстановить:
RestoreState (SaveState: TCanvasSaveState).
Сохранить и загрузить рисунок в Bitmap можно из Object Inspector или программно:
ABitmap. LoadFromFile ();
ABitmap.SaveToFile ();
ABitmap.SaveToStrem ().
Как отмечалось, во многих случаях удобнее отображать данные (которые удобно направлять в Memo) компонентом Path: TPathData, не конфликтующим с Path: TPath путем для файлов. Для этого вначале создаем глобальную переменную:
var Form1: TForm1; Path: TPathData;
При открытии на OnCreate в Form1 создаем траекторию:
Path:=TpathData.Create;
Подготавливаем сцену для визуализаци в OnPaint и Form1:
with Canvas do
if (Edit1.Text = «1») and (BeginScene) then
try
Stroke.Kind:=TBrushKind.Solid;
Stroke.Color:=TAlphaColors. Blue;
Stroke.Thickness:=2;
DrawPath (Path,1);
finally
endScene;
end;
Начало рисования ограничиваем Edit1.Text = «1», который контролируем Button.
Для рисования используем свойство OnPaint для Button:
procedure TForm1.Button3Paint (Sender: TObject; Canvas: TCanvas;
const ARect: TRectF);
var
P: TPointF;
i: Integer;
begin
P.X:= 0;
P.Y:= StrToInt(Memo1.Lines [0]);
Path.MoveTo (P);
for i:= 0 to Memo1.Lines.Count — 2 do
begin
P.X:= i*4;
P.Y:= PlotGrid1.Height — StrToInt(Memo1.Lines [i]);
Path.LineTo (P);
end;
end;
Результат можно видеть на рисунке: накапливаемые данные тут же отражаются на графике (перерисовывается график для данных в Memo пока Edit1.Text = «1»).
Все же, гораздо удобнее и проще использовать для вывода данных на график специальный компонент, для этого предназначенный: Chart.
11.5. Компонент построения графиков Chart
В Delphi Android имеются специальный компонент для построения графиков — Chart. В компоненте Chart многие десятки свойств и функций, но работать достаточно с несколькими. Нажимаем щелчком мыши по компоненту Chart и переходим во вкладку Series, выбираем кнопку Add: открывается список всех доступных графиков, выбираем, например, первый тип графика (линейный) — Line.
Здесь же видны другие первостепенные свойства Chart:
— Общие; — Оси, — Заглавия, — Легенды и пр.
Внутри группы открываются их подробные настройки, которые можно делать также из Object Inspector и программно.
Основная функция графика — добавление данных:
AddXY (AXValue, AYValue: Double; ALabel: String; AColor: TColor);
Chart1.Series[1].AddXY (x,y,» ',TAlphaColors.Red); //Например
Найти значение Y по X (от 0 до Х макс. видимого графика):
Y:= Chart1.Series [0].YValue [X]; или: Y:= Series1.YValue [X].
Следует учитывать, что: Series1 равна Chart1.Series [0].
Chart1.SaveToBitmapFile(FileName.jpg); //Сохранить график в файл
Series1.Delete (X); //Удалить точку X графика.
Series1.MaxYValue и *.MinYValue; // Y Max и Min видимого графика
Ymax и Ymin в пределах окна графика можно и вычислять:
Ymin:= 1000000; Ymax:= 0;
for X:= 1 to Chart1.BottomAxis.Maximum do
begin
if Ymax <Round(Chart1.Series [0].YValue [X]) then Ymax:= Round (Chart1.Series [0].YValue [X]);
if Ymin> Round(Chart1.Series [0].YValue [X]) then Ymin:= Round (Chart1.Series [0].YValue [X]);
end;
Chart1.LeftAxis.Maximum:= Ymax; (отключить автомасштаб!)
Chart1.LeftAxis. Minimum:= Ymin;
Вычислять положение точки Х для точки экрана:
Label1.Text :-= FloatToStr(Series1.MaxYValue);//Значение Ymax
Label1.Position. X:= Series1.CalcXPos (Round (Series1.MaxYValue)); //Положение метки на экране в точке Х соответствующей Значению Ymax.
11.6. КОМПОНЕНТЫ 3D ГРАФИКИ
В Delphi для Android 10 и 11, как и в более ранних версиях, предусмотрена также 3-х мерная графика, где представлены компоненты 3D-Shapes, с возможностью изменять цвет и структуру поверхности — группа Materials.
Для разработки 3D графики нужно использовать шаблон 3D Application или Viewport 3D. Тип проекции (свойство Projection) отображает в вариантах Camera (начало координат в центре экрана, координата Y снизу вверх), или Screen (координаты начинаются как обычно с верх- него левого угла экрана).
Вид объекта (глубина и ее направление) зависит от положе ния на экране. Координата Z приближает или отодвигает объект. Изменение угла отклоняет: для +X вперед, –Х назад; для Y соответственно вправо и влево: +Z поворачивает против часовой стрелки, –Z по часовой стрелке. Координаты объекта отсчитываются от его центра. Смысл всего этого для смартфонов проблематичен.
11.7. АНИМАЦИЯ ОБЪЕКТОВ
У многих компонентов есть свойство анимации (как и угла поворота). Это позволяет динамически придавать им различные функции — изменять положение, поворот, менять цвет. Также в группе Animation находится набор компонентов для придания анимации практически любым компонентам и многим свойствам (имеющим в Инспекторе объектов флажок — кадр фильма).
Для роста объекта можно менять его свойство Height, для изменения положения: Position. X и Position. Y, для поворота Rotation Center и Rotation Angle; соответственно, можно менять цвет — Color.
Компоненты группы Animation такие как: FloatAnimation и PathAnimation двигают объект от StartValue до StopValue по зависимости, определяемой свойством Interpolation (linear или exponent) и в течение секунд — Duration, с задержкой Delay. AutoReverse возвращает объект обратно по той же траектории, а Loop делает это циклически.
11.8. ВОЗМОЖНОСТЬ РИСОВАНИЯ НА ЭКРАНЕ
Для формы существует в Events событие OnTouch, позволяющее рисовать на экране в ответ на прикосновение и движение. Для этого:
Создаем глобальную переменную:
var
Form1: TForm1;
Path: TPathData;
При открытии на OnCreate в Form1 создаем траекторию:
Path:=TpathData.Create;
Подготавливаем сцену для визуализаци в OnPaint и Form1:
with Canvas do
if (Path.Count> 0) and (BeginScene) then
try
Stroke.Kind:=TBrushKind.Solid;
Stroke.Color:=TAlphaColors. Blue;
Stroke.Thickness:=2;
DrawPath (Path,1);
finally
endScene;
end;
Формируем событие OnTouch в Form1:
case Action of
TTouchAction.Down: begin
Path.MoveTo (Touches [0].Location);
end;
TTouchAction.Move: begin
Path.LineTo (Touches [High (Touches)].Location);
FormPaint(Sender,Form1.Canvas,Form1.ClientRect);
end;
В итоге движение пальца рисует синию линию (полилиния — Path).
Для изменения цвета и размера линии нужно ввести компоненты SpinBox и ColorListBox (обеспечивает выбор до 200 цветов), поместив их на Panel. Также нам понадобится Button для стирания рисунка:
Path.Clear; // Очистка компонента — полилиния (Path)
Path:=TpathData.Create; // Создание заново компонента Path
SpinBox1.Value:= 2; // Исходное значение величины кисти
ColorListBox1.Color:= TAlphaColors. Black; //Исходный цвет — черный
Важно: OnTouch реагирует на любое прикосновение к экрану, и чтобы рисунок не выводился на области Panel с компонентами настройки, ограничиваем выведение рисунка областью выше Panel:
if Touches [0].Location. Y <Panel1.Position. Y then
begin
case Action of
TTouchAction.Down: begin……………..
Для перехода в область настройки достаточно коснуться этой области внизу экрана (осторожно, чтобы не закрыть все приложение основными кнопками смартфона «>» и «O»), при этом исчезает рисунок и снова появляется при переходе в область рисивания. Однако, изменение цвета изменяет и цвет всех предыдущих линий, которые все перерисовываются заново с новыми установками цвета и размера линий.
Для того, чтобы рисовать другую линию, нужно ввести новый компонент Path1 (что вполне логично). Также нужно ввести понятие «сессии» и внести на Форму компонент Button для перехода к новой сессии и NumberBox для счета и отображения номера очередной сессии. Теперь на новую сессию рисуется новая линия с новыми настройками:
TTouchAction.Move: begin
if NumberBox1.Value = 1 then Path.LineTo (Touches [High (Touches)]. Location);
if NumberBox1.Value=2 then Path1.LineTo (Touches [High (Touches)]. Location);
Но теперь исчезли все предыдущие линии, что также понятно, так как каждый раз рисунок создается заново, и для того, чтобы были видны предудущие линии нужно, чтобы выводились на рисование ОБЕ линии:
with Canvas do
if (Path.Count> 0) and (BeginScene) then
try
Stroke.Kind:=TBrushKind.Solid;
if NumberBox1.Value = 1 then
begin
Stroke.Color:= TAlphaColors. Blue;
Stroke.Thickness:= 1;
DrawPath (Path,1);
end;
if NumberBox1.Value = 2 then
begin
Stroke.Color:= TAlphaColors. Blue;
Stroke.Thickness:= 1;
DrawPath (Path,1);
Stroke.Thickness:= 5;
Stroke.Color:= TAlphaColors.Red;
DrawPath (Path1,1);
end;
finally
endScene;
end;
В случае сессии 1 рисуется одна линия, а при сессии 2 рисуются (отображаются на экране) обе линии.
Для свободного выбора цветов введем компонент ColorListBox, а для изменения размера линии введем SpinBox. При этом нужно, чтобы они запоминались и считывались по мере увеличения числа сессий; при этом каждая сессия — новый компонент Path и новые установки рисования, причем все возможно задействованные в рисовании компоненты Path должны быть заранее объявлены и созданы как первый.
Новые сессии даже с аналогичным предудыщим старым набором свойств будут задействовать новый Path. Это касается и элемента «Ластик» который по существу является кистью с цветом фона (и настраиваемым размером); причем повторные обращения к Ластику приведут к новому задейстованному компоненту Path и новой сессии.
Для запоминания предудущих сессий подойдет элемент StringGrid, с простым обращением к его ячейкам — StringGrid. Cells [col, row]:
NumberBox1.Value :=NumberBox1.Value +1;//Новая сессия
StringGrid1.Cells [0, Round(NumberBox1.Value)]:= FloatToStr (SpinBox1.Value); //Новая сессия записывается по № сессии
StringGrid1.Cells [1, Round(NumberBox1.Value)]:= FloatToToStr (ColorListBox1.Color);
Для SpinBox1.Value размерность Double, что требует преобразовать его значение в FloatToStr () для таблицы, воспринимающей строковые данные. Для ColorListBox.Color цвет TAlphaColors может выражаться несколькими способами, в том числе цифрами, поэтому казалось бы возможно преобазование IntToStr (), так как цвет в TAlphaColors — это целое число, но это 10-значное число, превосходящее Integer, поэтому используется Double: FloatToStr () для записи, а для чтения из таблицы: Round (StrToFloat ()), так как цвет — это целое число, а Float дробное.
Для строки Таблицы используется номер сессии: NumberBox1.Value, причем, так как NumberBox1.Value имеет размерность Double, а для таблицы нужно целое число, можно NumberBox1.Value округлить: Round ().
В конечном итоге для 3-х сессий получим следующий код:
procedure TForm1.PaintBox1Paint (Sender: TObject; Canvas: TCanvas);
var
i: Integer;
begin
Sender:=PaintBox1;
with Canvas do
if (Path.Count> 0) and (BeginScene) then
try
Stroke.Kind:=TBrushKind.Solid;
if NumberBox1.Value = 1 then //Если первая сессия
begin //Начальные установки — черный цвет на блом фоне
Stroke.Color:= TAlphaColors. Black;
Stroke.Thickness:= 2;
DrawPath (Path,1);
end;
if NumberBox1.Value = 2 then
begin
Stroke.Color:= TAlphaColors. Black;//Сессия 1
Stroke.Thickness:= 2;
DrawPath (Path,1);
Stroke.Thickness:= StrToInt (StringGrid1.Cells [0, 2]); //Сессия 1
Stroke.Color:= Round (StrToFloat (StringGrid1.Cells [1, 2]));
DrawPath (Path1,1);
end;
if NumberBox1.Value = 3 then
begin
Stroke.Color:= TAlphaColors. Black; //Сессия 2
Stroke.Thickness:= 2;
DrawPath (Path,1);
Stroke.Thickness:= StrToInt (StringGrid1.Cells [0, 2]); //Сессия 2
Stroke.Color:= Round (StrToFloat (StringGrid1.Cells [1, 2]));
DrawPath (Path1,1);
Stroke.Thickness:= StrToInt (StringGrid1.Cells [0, 3]); //Сессия 3
Stroke.Color:= Round (StrToFloat (StringGrid1.Cells [1, 3]));
DrawPath (Path2,1);
end;
finally
endScene;
end;
end;
Таким образом, при увеличении числа сессий быстро нарастает сложность кода, когда каждая новая сессия включает в себя все предыдущие.
Приведенное приложение для рисования вполне работоспособно, сохранить рисунок можно используя PrintScreen экрана смартфона, но при наличии сложных и разнообразных программ для рисования программирование на Delphi Android в этом направлении — не более, чем ученичество, та как создать что-либо сложное проблематично.
Полный код программы для 5 сессий рисования показан ниже; обратите внимание, что программно изменен шрифт компонента ColorListBox, так как изначально он слишком велик и в Object Inspector нет возможности его изменить. Также следует поставить для NumberBox оба значения: HorzIncremet и VertIncrement = 0, так как даже при RedOnly:= true мы не избавляемся от реакции компонента на прикосновение к нему!
uses
System.SysUtils, System. Types, System. UITypes, System.Classes, System.Variants, FMX. Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX. Dialogs, FMX.StdCtrls, FMX.Controls.Presentation, FMX. Objects, System.ImageList, FMX.ImgList, FMX. Edit, FMX. EditBox, FMX. SpinBox, FMX.Layouts, FMX.ListBox, FMX.Colors, FMX.NumberBox, System. Rtti, FMX.Grid.Style, FMX.Grid, FMX.ScrollBox, FMX. Effects;
type
TForm1 = class (TForm)
Panel1: TPanel;
Button1: TButton;
************
private
{Private declarations}
public
{Public declarations}
end;
var
Form1: TForm1;
Path: TPathData;
Path1:TPathData;
Path2:TPathData;
Path3:TPathData;
Path4:TPathData;
implementation
{$R *.fmx}
//При открытии программы
procedure TForm1.FormCreate (Sender: TObject);
begin
Path:=TpathData.Create;
Path1:=TpathData.Create;
Path2:=TpathData.Create;
Path3:=TpathData.Create;
Path4:=TpathData.Create;
NumberBox1.Value:= 0;//Первая сессия не определена еще
ColorListBox1.Canvas.Font.Size:= 14;
end;
// Определение начальных точек для всех Path
procedure TForm1.FormTouch (Sender: TObject; const Touches: TTouches; const Action: TTouchAction);
var
i: Integer;
begin
if NumberBox1.Value = 0 then
begin
Label3.Visible:= true;//Предупреждение что сессий еще нет
EXIT;
end;
if Touches [0].Location. Y <Panel1.Position. Y then
begin
case Action of
TTouchAction.Down: begin
for i:= 1 to Round(NumberBox1.Value) do
begin
case i of
1: Path.MoveTo (Touches [0].Location);
2: begin
Path.MoveTo (Touches [0].Location);
Path1.MoveTo (Touches [0].Location);
end;
3: begin
Path.MoveTo (Touches [0].Location);
Path1.MoveTo (Touches [0].Location);
Path2.MoveTo (Touches [0].Location);
end;
4: begin
Path.MoveTo (Touches [0].Location);
Path1.MoveTo (Touches [0].Location);
Path2.MoveTo (Touches [0].Location);
Path3.MoveTo (Touches [0].Location);
end;
5: begin
Path.MoveTo (Touches [0].Location);
Path1.MoveTo (Touches [0].Location);
Path2.MoveTo (Touches [0].Location);
Path3.MoveTo (Touches [0].Location);
Path4.MoveTo (Touches [0].Location);
end;
end;
end;
end;
TTouchAction.Move: begin
for i:= 1 to Round(NumberBox1.Value) do
begin
case i of
1: Path.LineTo (Touches [High (Touches)].Location);
2: Path1.LineTo (Touches [High (Touches)].Location);
3: Path2.LineTo (Touches [High (Touches)].Location);
4: Path3.LineTo (Touches [High (Touches)].Location);
5: Path4.LineTo (Touches [High (Touches)].Location);
end;
end; end; end;
Бесплатный фрагмент закончился.
Купите книгу, чтобы продолжить чтение.