
Полная версия
Программирование для Android и работа с датчиками в среде Delphi 11
Компоненты 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 …. // в любом месте программы

Рис. 23. Элементы RadioButton в контейнере GroupBox позволяют сделать выбор.
CheckBox.
Флажок (независимый переключатель) отличается от выше описанного переключателя тем, что в группе флажков одновременно можно установить флажки в любой комбинации (в том числе могут быть установлены или сброшены все флажки).
Флажок может находиться в установленном или сброшенном состоянии. Одиночный флажок часто используется, например, для включения / выключения какого-либо режима. Флажок выглядит как прямоугольник с текстовым заголовком. Если в нем есть галочка, то обозначенная этим флажком опция включена (в этом случае также говорят, что флажок отмечен). Если прямоугольник пуст, то флажок снят, или сброшен. Дейстия с одним флажком не отражаются на состоянии других флажков, если это не было специально предусмотрено.
В Events используют свойства OnClick и OnChange, но обычно просто в программе используются состояния группы флажков и в зависимости от этого выполняются действия.
9. КОМПОНЕНТЫ ДЛЯ РАБОТЫ С ДАННЫМИ – Таблицы и Базы
Собственно, все компоненты текстового ввода/вывода рабо тают с данными, но традиционно именно Таблица предназначена для их накопления и отображения. Создать таблицу можно поместив ее на Форму и назначив число колонок через Items Editor, при этом могут быть назначены разные виды колонок для ввода разного типа данных (на практике ограничиваются обычной строковой колонкой и при необходимости преобразовани ем типов вводимых данных).

Рис. 24. Назначение колонок компоненту StringGrid через Item 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 с библиотеками.

Рис. 25. Смешанная таблица StringGrid.
10. ГЛАВНЫЙ КОМПОНЕНТ ДЛЯ ПРОГРАММИРОВАНИЯ – Button
Для активации какого-либо действия предназначен основной компонент Кнопка – Button, хотя практически у каждого компонента имеется обильный список свойств и возможностей, среди которых важнейшие: onClick, onChange и реакция на получение фокуса, вход в компонент и выход из него.
На примере Кнопки видны возможности разного визуального отображения компонентов: изменение цвета компонента и текста, размера компонента и шрифта, размытие (тень) и добавление иконки (через ImageListEditor), при этом размер иконки меняется с уменьшением кнопки (с увеличением – до размера самой иконки).
Добавление иконки (начальное значение ImageIndex:= -1 как отсутствие иконки) с помощью ImafeList.
Все эти художества, однако, не спасают компонент Кнопку и достичь обычного для Delphi вида не удается; она предстает как бледный полупрозначный плоский прямоугольник. Можно окрасить кнопку (свойство TintColor) и текст на ней, но это не намного улучшает вид кнопки, как и применение элементов из группы Effects.
Другие типы Кнопки из других групп также не лучше. Получить красивую Кнопку можно из компонента Панель, задействовав TBevelEffect для получения объемного вида, но другие эффекты не воспроизводятся, как и не меняется цвет панели; к тому же компонент Панель утратила текст на ней и цвет и для получения названия новоявленной Кнопки нужно сбросить на нее компонент Label. Кроме того, такая многоэтажная конструкция не только утяжеляет программу и сложность, но и непредсказуемо перестает работать).
Несколько лучше, чем в Delphi 10, ведут себя кнопки в Delphi 11, Android.

Рис. 26. Возможности компонентов в группе Events Инспектора объектов.
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).

Рис. 27. Загрузка в ImageControl рисунка через Image Editor.

Рис. 28. Использование Image Control для стационарного изображения эмблемы.
11.2. Группа компонентов Shape
Все фигуры можно взять готовыми из группы Shapes, что значительно упрощает работу с ними через готовые свойства и настройки. В Delphi Android, однако, компонент TPath, удобный для линейного графика, конфликтует с TPath классом, ответственным за работу с файлами, и System.IOUtils в Use.Прямоугольник Rectagle подойдет для отображения столбчатых графиков. Компонент можно сделать цветным и окрасить градиентом, например, в красный или зеленый цвет, показывая границы нормы.
Рис. 29. Построение графиков из элементов Rectangle по данным таблицы.
Для линейного графика можно использовать компонент 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. Повторные графики рисуются с сохранением предыдущих, причем большие графики изменяют масштаб предыдущих, а меньшие рисуются в пределах существующего.

Рис. 30. Построение графиков с помощью компонента Path1 на фоне компонента 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;

Рис. 31. Регистратор сигналов на базе компонента Path.
На рисунке представлен вид такого регистратора (в режиме теста – регистрация случайных данных – 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 при открытии приложения: