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
相关产品推荐
相关产品推荐

