如何将屏幕坐标转换为控制坐标

时间:2014-02-10 20:11:19

标签: android delphi delphi-xe5

我需要能够在同一时刻拦截平板电脑上的多个分接头的代码。在关于如何在同一时刻(不可能)处理多个OnMouseDown的previous question中,提供了link来回答关于如何处理Delphi-Android中的多点触控点击的问题。但是,此代码返回屏幕坐标中的(x,y)位置,我不知道如何将这些位置转换为特定控件的本地坐标。 Delphi documentation引用了ScreenToClient函数但只将屏幕坐标转换为表格坐标,这在Android中几乎没用(文档是关于XE2的,但该函数仍然存在于XE5中,但该函数已从FMX中删除。 FMX.Form的平台)。

有没有一种简单的方法可以将屏幕坐标转换为FMX中的TControl坐标,就像在VCL中一样?当然,我可以“取消父级”一个控件,记下它的左上角坐标,并为每个父级执行此操作,直到到达基本形式,但这非常繁琐。

修改1

我目前的方法是获取TControl(一个TPanel)的顶部(x,y)坐标,它是要点击的控件(实际上是TRectangle)的父级,并添加这些以检查点击是否在该矩形内。请参阅下面的示例代码

procedure TKeyBoard.process_touch (Event: TTouchEvent; status_byte: Int32);
var
  key: TKey;
  p, q: TPointF;
  i: Integer;
  x, y: single;
begin
// Check whether at least one event is present. If so, i points to the last event
   i := Length (Event.Points) - 1;
   if i < 0 then Exit;

// Get (x, y) coordinates from event. It's in screen coordinates
   x := Event.Points [i].Position.X;
   y := Event.Points [i].Position.Y;

// FControl is a reference to the panel holding the keys
// Find its rectangle position and convert to screen coordinates
   p := TPointF.Create (FControl.Position.X, FControl.Position.Y);
   q := TPointF.Create (p.X + FControl.Width, p.Y + FControl.Height);
   p := Application.MainForm.ClientToScreen (p);
   q := Application.MainForm.ClientToScreen (q);

   logd ('control [%.0f %.0f - %.0f, %.0f]', [FControl.Position.X, FControl.Position.Y, FControl.Width, FControl.Height]);
   logd ('to screen [%.0f %.0f - %.0f, %.0f]', [p.X, p.Y, q.X, q.Y]);

// Determine whether a black key has been pressed
   for i := Low (Fkeys) to High (FKeys) do
   begin
      if not cOctave_Major [i mod nOctave] then
      begin
         key := FKeys [i];

         logd ('%d (%.0f, %.0f) - (%.0f, %.0f) (%.0f, %.0f)', [i, x, y,
                  key.Position.X + p.X,             key.Position.Y + p.Y,
                  key.Position.X + P.X + Key.Width, key.Position.Y + p.Y + key.Height]);

         if (x >= key.Position.X + p.X) and (x <= key.Position.X + p.X + Key.Width) and
            (y >= key.Position.Y + p.Y) and (y <= key.Position.Y + p.Y + key.Height)
            then break;
         key := nil;
      end; // if
   end; // for

// if not, check whether a white key has been pressed
   if key = nil then
   begin
      logd ('Major');
      for i := Low (Fkeys) to High (FKeys) do
      begin
         if cOctave_Major [i mod nOctave] then
         begin
            key := FKeys [i];

            logd ('%d (%.0f, %.0f) - (%.0f, %.0f) (%.0f, %.0f)', [i, x, y,
                  key.Position.X + p.X,             key.Position.Y + p.Y,
                  key.Position.X + P.X + Key.Width, key.Position.Y + p.Y + key.Height]);

            if (x >= key.Position.X + p.X) and (x <= key.Position.X + p.X + Key.Width) and
               (y >= key.Position.Y + p.Y) and (y <= key.Position.Y + p.Y + key.Height)
               then break;
            key := nil;
         end; // if
      end; // for
   end; // if


   if key <> nil
      then putShort (status_byte, key.Note, 127);
   if key <> nil
      then logd (' found %s', [key.Text.Text]);
end; // process_touch //

这段代码实际上非常不整洁,因为它假定Parent控件将Application.MainForm作为其父级,但不一定是这种情况。另一个观察结果是,在Y位置,水龙头可能仍然略有错误。出于这个原因,我想将屏幕坐标直接传输到控件的坐标。

修改2

我尝试使用@Sentient建议的每个键控制的IsMouseOver检查,但奇怪的是,只有在处理MouseUp事件时才会产生true。

1 个答案:

答案 0 :(得分:9)

我是您正在使用的多点触控代码的作者。当我看到你在坐标上挣扎时,我看了可以做什么并更新了代码,所以它现在为你提供了触摸控件和相对坐标。此外,如果你想知道如何简单。

关于它的博客文章在这里:

http://www.cromis.net/blog/2014/02/multi-touch-touched-control-and-relative-coordinates/

解决它的代码如下所示:

  if Screen.ActiveForm <> nil then
  begin
    for I := 0 to Length(Event.Points) - 1 do
    begin
      Control := Screen.ActiveForm.ObjectAtPoint(Event.Points[I].Position);

      if Control <> nil then
      begin
        Event.Points[I].Control := Control as TFmxObject;
        Event.Points[I].RelPosition := Control.ScreenToLocal(Event.Points[I].Position);
      end
      else
      begin
        Event.Points[I].Control := Screen.ActiveForm;
        Event.Points[I].RelPosition := Screen.ActiveForm.ScreenToClient(Event.Points[I].Position);
      end;
    end;
  end;