• 0
rareMax

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

Вопросы

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

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

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты

7 ответов на этот вопрос

  • 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);

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
  • 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;

Поделиться сообщением


Ссылка на сообщение
Поделиться на другие сайты
Гость
Эта тема закрыта для публикации ответов.

  • Похожий контент

    • От stoi
      Задача: Сделать обработку длинного нажатия. Планшет Microsoft Surface, ОС Windwos 10.
      Изначально алгоритм такой:
      По MouseDown запускаем таймер, по MouseUp - анализируем. Если прошло более 0.8 sec - это было длинное нажатие. Есть примеры в сети. Под Андроид всё работает. На десктопной Windows 10 - тоже.
      На планшете Microsoft Surface - не работает. События приходят в следующем порядке:
      Короткое нажатие:
      13:09.08.518: Tap 13:09.08.518: MouseDown 13:09.08.518: Click 13:09.08.518: MouseUp   Длинное нажатие:   13:09.18.327: Tap 13:09.18.327: MouseDown 13:09.18.327: MouseUp   Такое ощущение, что они прилетают единомоментно при отпускании (MouseUp). Что можно сделать? Как правильно отследить длинное нажатие в данном случае? Это баг или фича Delphi?   Спасибо!
    • От denprox
      Доброго времени суток! Подскажите, как получить хендл (HWND) контрола, например TImage расположенного на форме ? 
    • От AliZairov
      Привет. Я делаю Android компонент. Я использовал TControl как класс. Проблемы TControl памяти.
      property Position stored True; property Width stored True; property Height stored True; constructor TAZVideoPlayer.Create(AOwner: TComponent); begin inherited; Log.d ( Round(Self.Position.X).ToString + 'x' + Round(Self.Position.Y).ToString + '_' + Round(Self.Width).ToString + 'x' + Round(Self.Height).ToString ); end; var V: TAZVideoPlayer; begin V := TAZVideoPlayer.Create(Self); V.Position.X := 112; V.Position.Y := 112; V.Width := 100; V.Height := 100; Настройки не будут записаны. Где я ошибаюсь?
      Logcat: FMX: App: 0x0_50x50
    • От Alex7wrt
      Здравствуйте.
      Нужно мышкой менять размеры контролов, которые имеют общую границу. Как распространить событие OnMouseMove для одного контрола за его пределы? Чтобы можно было отловить это событие, когда мышь находится на соседнем контроле.
    • От SergeyIT
      В известном видео
      https://www.youtube.com/watch?v=ta_N6DSi0Xg
      представлена техника перетаскивания графических объектов типа TRectangle, TEllipse.
      Почему она не работает для тех же объектов, но созданных программным путем, скажем, для TLine?
      Быть может есть ключевое свойство, связанное с Captured особенностями.
       
      Ухищрения с координатами также мало помогают. В лучшем случае все выглядит как перемещение с эффектом стробоскопа - объект движется, но "мигает" по 2-4 позициям.
    • От Вячеслав
      Господа, проблемы с сабжем. Не работает и все тут. Может я туплю и в интернетах описаны способы регистрации события у трекбара, но у меня пока не получается. Интересует отлов отпускания мыши у компонента, созданного в Design-Time.
    • От antarey
      В андроид апи  в классе  android.view.View  есть процедура public void startAnimation (Animation animation)
       
      Если написать обертку для android.view.animation.Animation можно передать startAnimation (Animation animation) в TControl и главное как?
      чтобы получилось как на яве
      mView1.startAnimation(anim);
    • От Andrey Efimov
      Ярослав, прочитал ваш совет http://fire-monkey.ru/topic/95-kak-pravilno-zamenit-image-na-webbrowser/?p=297
       
      Интересен вот этот пункт:
       
       
      А точнее, как определить положение картинки в ListBoxItem, если картинка у нас находится тут "TListBoxItem.ItemData.Bitmap"?
  • Последние посетители   0 пользователей онлайн

    Ни одного зарегистрированного пользователя не просматривает данную страницу