Некоторые наблюдения по итогам тестовых приложений для Android. Мой взгляд на разработку.
function ShowModal: TModalResult; overload;
procedure ShowModal(const ResultProc: TProc<TModalResult>); overload;
с помощью них можно эмулировать поведение модальных окон, т.е вынести код после then в анонимный метод.
Обратите внимание, что код почти идентичен. Единственная различается в том, что код выполняется в второй форме (псевдо диалоговой), а не в первой как в классическом случае. Фактически мы как бы код передаем второй форме. Однако фактически хоть мы и эмулируем логику модальной форму - на самом деле это обычная форма с хитрым механизмом закрытия.
2.Прокрутка
Тут все очень просто, есть ScrollBox и похожие по функционалу VerticalScrollBox и т.д. Соответственно, если в форму у вас не влазят в дизайнере компоненты то их нужно формировать в рунтайме, что не совсем удобно или расширить место при помощи трюка (см.далее).
Тривиальный пример формирования 50 кнопок (в дизайнере не влезут на экран) в окне вертикальной прокрутки:
Upd:1
3.Стили
4.Жесты
Продолжение следует.
Тут решил собирать некоторые заметки о разработке для Android.
1.Формы в мобильных устройствах -
Формы в мобильных устройствах лучше создавать динамически, т.к. расход памяти все таки критичен. Еще одной особенностью форм в Android-платформе является отсутствие в платформе поддержки диалоговых форм.
Т.е. классически ShowModal вы использовать не сможете, сработает только метод Show. Соответственно, это накладывает некоторые ограничения например для окон настройки. Теперь изменяемые данные в псевдо-диалоге не могут быть интерпретированы как константные, т.к. форма может быть переключена, перекрыта другой, закрыта и т.п.
Поэтому нужно заботится о том, что любое изменение на псевдо-диалоге должно вызывать оповещение зависящих от ее данных форм и контролов. Можно методами сообщений. Вообщем в отличии от десктоп-платформы это накладывает дополнительные расходы на код.
Upd:1
Как мне подсказали в комментариях, в Android системах для FM изменен механизм работы ShowModal. Теперь классический способ вызова, как мы все привыкли ранее:
if fDlfForm.ShowModal = mrOk
вызовет ошибку "ShowModal not implemented on this platform"
Это связано с тем, что в Windows системах, как и в Apple-системах в отличии от Android-а есть цикл обработки сообщений для каждого отдельного окна.
Т.е. метод ShowModalостанавливает прием сообщений в первичной форме и обрабатывает их в вызываемой форме (вторичной).
Однако в Androide не такой системы обработки сообщений, что делает решение модальных форм с одной стороны очень сложным. Однако есть способ обойти это ограничение - это новые перегруженные методы
function ShowModal: TModalResult; overload;
procedure ShowModal(const ResultProc: TProc<TModalResult>); overload;
с помощью них можно эмулировать поведение модальных окон, т.е вынести код после then в анонимный метод.
procedure TForm1.Button3Click(Sender: TObject);
var
fFormDlg: TFormAdditional;
begin
Try // Классический метод на Android устройствах не сработает
fFormDlg := TFormAdditional.Create(nil);
fFormDlg.checkBox.IsChecked := True;
IF fFormDlg.ShowModal = mrOk then
begin
if fFormDlg.checkBox.IsChecked then
LabelInfo.Text := 'Ckecked'
else
LabelInfo.Text := 'UnCkecked';
end
else
LabelInfo.Text := '-';
Finally
fFormDlg.DisposeOf;
End;
end;
procedure TForm1.Button2Click(Sender: TObject);
var
fFormDlg: TFormAdditional;
begin
fFormDlg := TFormAdditional.Create(nil);
fFormDlg.checkBox.IsChecked := True;
// Анонимный метод на Android устройствах сработает
fFormDlg.ShowModal(
procedure(ModalResult: TModalResult)
begin
if ModalResult = mrOK then
begin
if fFormDlg.checkBox.IsChecked then
LabelInfo.Text := 'Ckecked'
else
LabelInfo.Text := 'UnCkecked';
end
else
LabelInfo.Text := '-';
fFormDlg.DisposeOf;
end
);
end;
Обратите внимание, что код почти идентичен. Единственная различается в том, что код выполняется в второй форме (псевдо диалоговой), а не в первой как в классическом случае. Фактически мы как бы код передаем второй форме. Однако фактически хоть мы и эмулируем логику модальной форму - на самом деле это обычная форма с хитрым механизмом закрытия.
2.Прокрутка
Тут все очень просто, есть ScrollBox и похожие по функционалу VerticalScrollBox и т.д. Соответственно, если в форму у вас не влазят в дизайнере компоненты то их нужно формировать в рунтайме, что не совсем удобно или расширить место при помощи трюка (см.далее).
Тривиальный пример формирования 50 кнопок (в дизайнере не влезут на экран) в окне вертикальной прокрутки:
procedure TForm1.FormCreate(Sender: TObject);
var
I : Integer;
_Btn : TButton;
begin
for I := 0 to 50 do
begin
_Btn := TButton.Create(Self);
_Btn.Parent := ScrollBox;
_Btn.Text := 'Кнопка: ' + IntToStr(I);
_Btn.Height := 50;
_Btn.Width := 200;
_Btn.Position.X := 0;
_Btn.Position.Y := (I * 50) + 1;
end;
end;
Upd:1
Маленькая хитрость или трюк трюк по расширению рабочего места в дизайнере: Если положить ScrollBox и на него кинуть например кнопку, предварительно растянув ее по высоте максимально, после чего перетащить ее за верхнюю часть вниз, то в дизайнере в ScrollBox-е действительно появляется дополнительное место, куда можно положить другие компоненты. Начинает работать прокрутка колесом мыши. После чего кнопку которая помогла нам расширить место в дизайнере можно убрать. Вот такая хитрость проектирования в дизайнере.
3.Стили
Тут все просто StyleBook на форму и связь с ним в форме. Однако есть некоторые ньюансы, Delphi XE5 иногда глючит если в свойстве Fill.Kind стоит отличное от bkNone значение. То есть возможно появление артефактов когда часть объектов (контролов) на фоме стала под стиль, а сама форма нет. Причем если в дизайнере вид более менее под стиль, то в устройстве вообще может быть по другому
4.Жесты
Поддержка жестов реализовано через TGestureManager.
Нам доступны как стандартные жесты (Standard) так и интерактивные жесты (InteractiveGestures) такие как маштабирование, нажатие + касание и т.п.
Стоит отметить, что если вы совмещаете стандартные и интерактивные жесты могут возникнуть коллизии, например если вы выставляете интерактивный жест igPan и используете стандартные действия влево вправо вверх и вниз, то нормально обрабатывать перемещение некого контрола вы не сможете, т.к. например делая жест влево с целью переместить например некий объект у вас сработает свойство обработки OnGesture c иденификатором sgiLeft а не ожидаемый igPan. Поэтому продумывайте какой тип жестов вы хотите интерактивный или стандартный в поведении контрола.
Данную коллизию жестов хорошо иллюстрирует простейший пример:
type
TForm1 = class(TForm)
Rectangle: TRectangle;
GestureManager: TGestureManager;
procedure RectangleGesture(Sender: TObject;
const EventInfo: TGestureEventInfo; var Handled: Boolean);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
SizeX,SizeY : Integer;
procedure MyZoom(const eventInfo: TGestureEventInfo);
procedure MyPan(const eventInfo: TGestureEventInfo);
procedure MyUp;
procedure MyDown;
procedure MyLeft;
procedure MyRight;
procedure MyPressAndTap;
public
{ Public declarations }
LastPosition: TPointF;
LastDistance: Integer;
end;
var
Form1: TForm1;
implementation
{$R *.fmx}
procedure TForm1.FormCreate(Sender: TObject);
begin
SizeX := Screen.Size.Width;
SizeY := Screen.Size.Height - 22;
LastPosition := Rectangle.Position.Point;
end;
procedure TForm1.MyPressAndTap;
begin
ShowMessage('Press and Tap');
end;
procedure TForm1.MyUp;
begin
// Последовательная анимация для нелинейной скорости с замедлением в конце в течении
// 1 секунды
Rectangle.AnimateFloat('Position.Y', 0 , 1.0, TAnimationType.atOut, TInterpolationType.itQuadratic);
LastPosition := Rectangle.Position.Point;
end;
procedure TForm1.MyRight;
begin
Rectangle.AnimateFloat('Position.X',
SizeX - Rectangle.Width,
1.0, TAnimationType.atOut, TInterpolationType.itQuadratic);
LastPosition := Rectangle.Position.Point;
end;
procedure TForm1.MyDown;
begin
Rectangle.AnimateFloat('Position.Y',
SizeY - Rectangle.Height,
1.0, TAnimationType.atOut, TInterpolationType.itQuadratic);
LastPosition := Rectangle.Position.Point;
end;
procedure TForm1.MyLeft;
begin
Rectangle.AnimateFloat('Position.X', 0,
1.0, TAnimationType.atOut, TInterpolationType.itQuadratic);
LastPosition := Rectangle.Position.Point;
end;
// Маштабирование объекта
procedure TForm1.MyZoom(const eventInfo: TGestureEventInfo);
var
NewDistance : Single;
begin
NewDistance := EventInfo.Distance - LastDistance;
Rectangle.Width := Rectangle.Width + (NewDistance );
Rectangle.Height := Rectangle.Height + (NewDistance );
Rectangle.Position.X := Rectangle.Position.X - (NewDistance / 2);
Rectangle.Position.Y := Rectangle.Position.Y - (NewDistance / 2);
// Запоминаем дистанцию нашего Rectangle
LastDistance := EventInfo.Distance;
end;
// Перемещение объекта
procedure TForm1.MyPan(const eventInfo: TGestureEventInfo);
begin
Rectangle.Position.X := Rectangle.Position.X + (EventInfo.Location.X - LastPosition.X);
if Rectangle.Position.X < 0 then
Rectangle.Position.X := 0;
if Rectangle.Position.X > (SizeX - Rectangle.Width) then
Rectangle.Position.X := SizeX - Rectangle.Width;
Rectangle.Position.Y := Rectangle.Position.Y + (EventInfo.Location.Y - LastPosition.Y);
if Rectangle.Position.Y < 0 then
Rectangle.Position.Y := 0;
if Rectangle.Position.Y > (SizeY - Rectangle.Height) then
Rectangle.Position.Y := SizeY - Rectangle.Height;
LastPosition := EventInfo.Location;
end;
procedure TForm1.RectangleGesture(Sender: TObject; const EventInfo: TGestureEventInfo; var Handled: Boolean);
begin
if EventInfo.GestureID = igiZoom then MyZoom(EventInfo) else
if EventInfo.GestureID = igiPan then MyPan(EventInfo) else
if EventInfo.GestureID = igiPressAndTap then MyPressAndTap else
if EventInfo.GestureID = igiPressAndTap then MyPressAndTap else
if EventInfo.GestureID = sgiUp then MyUp else
if EventInfo.GestureID = sgiDown then MyDown else
if EventInfo.GestureID = sgiLeft then MyLeft else
if EventInfo.GestureID = sgiRight then MyRight;
end;
В этом примере хорошо видно, что начать нормально перемещать Rectangle вы можете только по диагонали, а не по вертикали и горизонтали. Нормального перемещения не получается - при жестах перемещения вверх, вниз, влево, вправо наш объект прыгает и в конце концов уезжает. Однако выключив стандартные свойства отвечающие за поведение жестов - вверх, вниз, влево, вправо - вы добьетесь номального перемещения rectangle.
Выставлены стандартные жесты:
Вместе с ними выставлены интерактивные жесты - особое внимание на igPan:
Касательно модальных форм замечание не совсем корректное. Их использовать можно и нужно...
ОтветитьУдалитьПосмотрите на вот эту статейку: http://www.delphifeeds.com/go/s/108702
Дело в том, что вызов ShowModal выдал на телефоне сообщение о неподдержке ее в этой платформе. Судя по статье там работа с ShowModal идет через анонимные методы, интересная концепция, попробуем новый механизм ShowModal.
ОтветитьУдалить> 2.Прокрутка
ОтветитьУдалить>... Соответственно, если в форму у вас не влазят в дизайнере компоненты то их нужно формировать в рунтайме, что не совсем удобно. ...
я тоже так по началу подумал, а нет. В дизайнере пользуемся скролом мышиным, только курсор мышки должен быть обязательно над компонентом. :)
Получилось но не совсем так. Просто скролл над компонентом не сработал. Однако если положить скроллбокс и на него кинуть например кнопку, предварительно растянув ее по высоте максимально возможным способом, после чего перетащить ее вниз, то в скроллбоксе действительно появляется место для в дизайнере, куда можно положить другие компоненты. После чего кнопку которая помогла нам расширить место в дизайнере можно убрать. Вот такая хитрость проектирования в дизайнере.
ОтветитьУдалитьда именно так я и пользуюсь. Бросаю несколько кнопок на TScrollBox и разношу их за пределы насколько это возможно.
Удалить