You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Delphi FMX Windows触屏绘图应用:寻求触摸开始/结束事件实现方案

解决Delphi FMX触摸屏触摸开始/结束事件的方案

针对你在带触摸屏的Windows电脑上开发FMX绘图应用时遇到的触摸事件问题,我整理了几个可靠的实现途径,完美替代MouseDown/MouseUp并支持TControl.Pressed属性:

1. 直接使用FMX的OnTouch事件

FMX控件自带的OnTouch事件是专门为触摸输入设计的,能精准捕获触摸的开始、移动和结束状态,不受鼠标事件兼容性问题影响。

用法示例:

procedure TForm1.PaintBox1Touch(Sender: TObject; const EventInfo: TTouchEventInfo;
  var Handled: Boolean);
begin
  case EventInfo.EventKind of
    TTouchEventKind.Down:
    begin
      // 触摸开始,类似MouseDown
      PaintBox1.Pressed := True;
      // 记录初始触摸坐标,启动绘图逻辑
      FStartPoint := EventInfo.Location;
    end;
    TTouchEventKind.Up:
    begin
      // 触摸结束,类似MouseUp
      PaintBox1.Pressed := False;
      // 收尾绘图操作
    end;
    TTouchEventKind.Move:
    begin
      if PaintBox1.Pressed then
      begin
        // 处理触摸拖动时的连续绘图
        DrawLine(FStartPoint, EventInfo.Location);
        FStartPoint := EventInfo.Location;
      end;
    end;
  end;
  Handled := True; // 标记事件已处理,避免后续手势逻辑干扰
end;

这个事件能覆盖所有触摸场景,包括拖动时的连续输入,完全适配触摸屏设备。

2. 重写控件的Touch虚方法

如果需要更底层的控制,可以重写TControl的Touch虚方法,绕过FMX的手势处理逻辑,直接响应触摸输入。

比如自定义一个绘图专用的TPaintBox:

type
  TCustomPaintBox = class(TPaintBox)
  private
    FOnTouchDown: TNotifyEvent;
    FOnTouchUp: TNotifyEvent;
  protected
    procedure Touch(const EventInfo: TTouchEventInfo; var Handled: Boolean); override;
  public
    property OnTouchDown: TNotifyEvent read FOnTouchDown write FOnTouchDown;
    property OnTouchUp: TNotifyEvent read FOnTouchUp write FOnTouchUp;
  end;

procedure TCustomPaintBox.Touch(const EventInfo: TTouchEventInfo; var Handled: Boolean);
begin
  inherited;
  case EventInfo.EventKind of
    TTouchEventKind.Down:
    begin
      Pressed := True;
      if Assigned(FOnTouchDown) then FOnTouchDown(Self);
      Handled := True;
    end;
    TTouchEventKind.Up:
    begin
      Pressed := False;
      if Assigned(FOnTouchUp) then FOnTouchUp(Self);
      Handled := True;
    end;
  end;
end;

这种方式适合需要高度自定义触摸行为的场景,完全不受Tap或GestureManager的限制。

3. 拦截Windows原生WM_TOUCH消息

如果上述FMX自带方法仍有兼容性问题(比如某些特殊触摸屏驱动),可以直接在窗体的WndProc中拦截Windows原生触摸消息,解析触摸状态后转化为自定义事件。

示例代码:

type
  TForm1 = class(TForm)
    PaintBox1: TPaintBox;
  protected
    procedure WndProc(var Message: TMessage); override;
  end;

procedure TForm1.WndProc(var Message: TMessage);
const
  WM_TOUCH = $0240;
var
  TouchInputCount: Integer;
  TouchInputs: array of TTouchInput;
  I: Integer;
  TouchPoint: TPointF;
begin
  if Message.Msg = WM_TOUCH then
  begin
    TouchInputCount := Message.WParam;
    SetLength(TouchInputs, TouchInputCount);
    try
      if GetTouchInputInfo(Message.LParam, TouchInputCount, @TouchInputs[0], SizeOf(TTouchInput)) then
      begin
        for I := 0 to TouchInputCount - 1 do
        begin
          // 转换触摸坐标为FMX客户端坐标
          TouchPoint := ScreenToClient(Point(TouchInputs[I].x div 100, TouchInputs[I].y div 100));
          if (TouchInputs[I].dwFlags and TOUCHEVENTF_DOWN) <> 0 then
          begin
            PaintBox1.Pressed := True;
            // 触发触摸开始的自定义逻辑
          end
          else if (TouchInputs[I].dwFlags and TOUCHEVENTF_UP) <> 0 then
          begin
            PaintBox1.Pressed := False;
            // 触发触摸结束的自定义逻辑
          end;
        end;
        CloseTouchInputHandle(Message.LParam);
      end;
      Message.Result := 0;
      Exit;
    finally
      SetLength(TouchInputs, 0);
    end;
  end;
  inherited;
end;

注意需要在uses中引入Windows单元,这种方法直接和Windows触摸驱动交互,兼容性最强。

额外注意事项

  • 确保项目的TouchGestureMode属性设置为TGestureMode.Default或TGestureMode.None,避免手势管理器屏蔽触摸事件;
  • 测试时优先使用OnTouch事件,它是FMX官方推荐的触摸处理方式,适配性最好;
  • 如果需要处理多点触摸,可以在OnTouch事件中通过EventInfo.TouchID区分不同的触摸点。

内容的提问来源于stack exchange,提问作者SuckLips

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:23:46