Перейти к содержанию
Fire Monkey от А до Я
  • 0

Как сделать перемещение контрола мышкой/пальцем?


rareMax

Вопрос

Как лучше сделать перемещение контролов на форме с помощью "пальца", мыши или стилуса?

Изменено пользователем admin
Изменена постановка вопроса
Ссылка на комментарий

Рекомендуемые сообщения

  • 0
  • Администраторы

Добрый вечер,
 
Перемещение контрола можно сделать двумя способами:

  • Используя обычный набор событий OnMouseDown, OnMouseMove, OnMouseUp.
  • Использовать для перемещения жесты.

1. Реализация с использованием событий мыши
Эти события реализованы для всех платформ. В том числе они эмулируются на мобильных платформ, где понятия мыши, как такового нету. Это означает, что их можно использовать для реализации перетаскивания контрола. Это можно сделать, например, следующим образом:
 
а) Создаем форму и кидаем на нее картинку. Я назвал ее DraggableImage.
post-1-0-61894300-1389811271_thumb.jpg
б) Заводим два поля.

  TForm5 = class(TForm)
    DraggableImage: TImage;
    procedure DraggableImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
    procedure DraggableImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
    procedure FormCreate(Sender: TObject);
    procedure DraggableImageMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
  private
    FStartPos: TPointF;
    FPressed: Boolean;
  end;
  • FStartPos - начальная локальная позиция мыши внутри нашего контрола (картинки), когда пользователь зажал кнопку мышки или опустил палец на экран.
  • FPressed - флаг для сигнализирования, что пользователь опустил палец (зажал кнопку мыши) на нашу картинку и до текущего момента не снял с экрана.

в) В конструкторе формы для перетаскиваемого контрола ОБЯЗАТЕЛЬНО задаем AutoCapture = True. Это позволит контролу генерировать события перемещения мыши, даже если курсор мыши ушел за локальные границы контрола.

procedure TForm5.FormCreate(Sender: TObject);
begin
  DraggableImage.AutoCapture := True;
end; 

г) В момент нажатия на картинку сохраняем локальную позицию мыши и задаем флаг FPressed = True

procedure TForm5.DraggableImageMouseDown(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
  FPressed := True;
  FStartPos := TPointF.Create(X, Y);
end;

д) В момент отпускания кнопки мыши или убирания пальца с экрана сбрасываем флаг FPressed:

procedure TForm5.DraggableImageMouseUp(Sender: TObject; Button: TMouseButton; Shift: TShiftState; X, Y: Single);
begin
  FPressed := False;
end;

е) И собственно меняем позицию картинки, когда мы ведем мышкой по картинке:

procedure TForm5.DraggableImageMouseMove(Sender: TObject; Shift: TShiftState; X, Y: Single);
var
  MoveVector: TVector;
begin
  if FPressed then
  begin
    // Вычисляем локальное смещение относительно первоначальной позиции
    MoveVector := TVector.Create(X - FStartPos.X, Y - FStartPos.Y, 0);

    // Вычисляем смещение в координатах формы, чтобы учесть изменение
    // координат при смещении родительских контролов
    MoveVector := DraggableImage.LocalToAbsoluteVector(MoveVector);
    if DraggableImage.ParentControl <> nil then
      MoveVector := DraggableImage.ParentControl.AbsoluteToLocalVector(MoveVector);
    // Перемещаем картинку на вычисленный вектор 
    // Для RAD Studio XE5
    DraggableImage.Position.Point := DraggableImage.Position.Point + MoveVector.ToPointF; 
    // Для новых версий
    // DraggableImage.Position.Point := DraggableImage.Position.Point + TPointF(MoveVector); 
  end; 
end;

Этот кусок стоит прокомментировать, чтобы корректно выполнить перетаскивание контролу обязательно нужно вычислять смещение в абсолютных координатах формы. Причина в том, что если контрол повернут или входит один в другой, то нужно учитывать смещения всей цепочки родительских контролов до формы. Поэтому мы вначале вычисляем смещение в локальных координатах, затем вычисляем его в абсолютных координатах формы. А затем обратно переводим в локальные координаты родительского контрола. После чего изменяем позицию контрола на вычисленное смещение.
 
Такой подход используется, в частности, в контроле TSelection, который так же можно перемещать мышкой или пальцем.
 
P.S. Чтобы лучше понять это, нарисуйте на листке бумаги положения контрола и попробуйте вручную выполнить этот алгоритм с преобразованием координат. 
 
Собственно говоря, такой подход отлично работает везде и не требует использования системы жестов.

2. Реализация с использованием жестов

Этот способ будет работать только на мобильных платформах, поскольку нужный жест Pan (если я правильно помню) не поддерживается под Windows. Поэтому в целом, первый вариант является универсальным и лучшим решением. 

Ссылка на комментарий
  • 0
  • Администраторы

Отличный пример! Вопрос у меня XE5 и он требует ввести координату Z в функции MoveVector := TVector.Create(X - FStartPos.X, Y - FStartPos.Y);

 

В 2D приложения третья ось не учитывается, поэтому можете в конструкторе указать, что она равна 0.

MoveVector := TVector.Create(X - FStartPos.X, Y - FStartPos.Y, 0); 

Либо вместо TVector использовать TPointF. В этом случае нужно будет заменить вызовы:

DraggableImage.LocalToAbsoluteVector(MoveVector);
DraggableImage.ParentControl.AbsoluteToLocalVector(MoveVector);

на

DraggableImage.LocalToAbsolute(MovePoint);
DraggableImage.ParentControl.AbsoluteToLocal(MovePoint);

Где MovePoint: TPointF;

Ссылка на комментарий
  • 0

если добавить ось Z:=0

 

Обработчик грешит вот на эту строку

DraggableImage.Position.Point := DraggableImage.Position.Point + TPointF(MoveVector);
[dcc32 Hint] First.pas(42): H2219 Private symbol 'MouseDownPoint' declared but never used
[dcc32 Hint] First.pas(44): H2219 Private symbol 'Moving2' declared but never used
[dcc32 Hint] First.pas(46): H2219 Private symbol 'Splitting' declared but never used
 
 
Если изменить код по вашему совету....то ругается на ту-же строку и в обработчике та-же ошибка:(
Ссылка на комментарий
  • 0
  • Администраторы

Используйте тогда явную конвертацию вектора в точку:

DraggableImage.Position.Point := DraggableImage.Position.Point + MoveVector.ToPointF;

P.S. В приведенном мною коде, нету неиспользуемых переменных MouseDownPoint, Moving2 и Splitting.

P.S.S. Текст ошибки все-таки не привели, что именно говорит компилятор.

Ссылка на комментарий
  • 0

Да и вот весь код!

procedure TForm4.DraggableImageMouseMove(Sender: TObject; Shift: TShiftState; X,
  Y: Single);
var
  MoveVector: TVector;
begin
  if FPressed then
  begin
    // Вычисляем локальное смещение относительно первоначальной позиции
    MoveVector := TVector.Create(X - FStartPos.X, Y - FStartPos.Y,0);

    // Вычисляем смещение в координатах формы, чтобы учесть изменение
    // координат при смещении родительских контролов
    MoveVector := DraggableImage.LocalToAbsoluteVector(MoveVector);
    if DraggableImage.ParentControl <> nil then
      MoveVector := DraggableImage.ParentControl.AbsoluteToLocalVector(MoveVector);
    // Перемещаем картинку на вычисленный вектор
    DraggableImage.Position.Point := DraggableImage.Position.Point + MoveVector.ToPointF;
  end;
end;
Ссылка на комментарий
Гость
Эта тема закрыта для публикации ответов.
×
×
  • Создать...