Некоторые наблюдения по итогам тестовых приложений для 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 и разношу их за пределы насколько это возможно.
Удалить