Delphi FMX:点击按钮触发TPaintBox绘图(不使用OnPaint事件)
问题
不想使用onPaint事件在TPaintBox上绘图,希望点击按钮后再执行绘图操作,如何实现?当前代码已添加按钮的btnPaintSmileClick事件,需要通过该事件激活绘图。
解决方案
实现思路
FireMonkey中TPaintBox的OnPaint事件是系统触发重绘时自动调用的,要实现点击按钮才绘图,最稳妥的方式是:
- 新增私有布尔变量标记是否需要绘制笑脸
- 按钮点击时设置标记为
True,并调用PaintBox1.Repaint()触发重绘 - 修改
OnPaint事件代码,仅当标记为True时才执行绘图逻辑
修改后的完整代码
unit Unit1; interface uses System.SysUtils, System.Types, System.UITypes, System.Classes, System.Variants, System.Math, FMX.Types, FMX.Controls, FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Controls.Presentation, FMX.StdCtrls, FMX.Objects; type TForm1 = class(TForm) PaintBox1: TPaintBox; btnPaintSmile: TButton; procedure PaintBox1Paint(Sender: TObject; Canvas: TCanvas); procedure btnPaintSmileClick(Sender: TObject); private FDrawSmile: Boolean; // 新增绘制标记变量 public end; var Form1: TForm1; implementation {$R *.fmx} procedure TForm1.btnPaintSmileClick(Sender: TObject); begin FDrawSmile := True; // 开启绘制标记 PaintBox1.Repaint(); // 触发PaintBox重绘 end; procedure TForm1.PaintBox1Paint(Sender: TObject; Canvas: TCanvas); var DrawRect, faceRect, eyeRectL, eyeRectR: TRectF; s, radius, lineW, eyeR, eyeOffsetX, eyeOffsetY: Single; c: TPointF; FaceColor, EyeColor, LineColor: TAlphaColor; WithOutline: Boolean; begin // 仅当标记为True时执行绘图逻辑 if not FDrawSmile then Exit; DrawRect := PaintBox1.LocalRect; DrawRect.Inflate(-8, -8); if (Canvas = nil) or (DrawRect.Width <= 0) or (DrawRect.Height <= 0) then Exit; // Settings FaceColor := $FFFFCC4D; // Emoji yellow EyeColor := $FF000000; // Black LineColor := $FF7A5200; // Warm brown outline WithOutline := True; // Scale & center s := Min(DrawRect.Width, DrawRect.Height); c := PointF( DrawRect.Left + s * 0.5 + (DrawRect.Width - s) * 0.5, DrawRect.Top + s * 0.5 + (DrawRect.Height - s) * 0.5 ); radius := 0.5 * s; // Face faceRect := RectF( c.X - radius, c.Y - radius, c.X + radius, c.Y + radius ); Canvas.Fill.Kind := TBrushKind.Solid; Canvas.Fill.Color := FaceColor; Canvas.FillEllipse(faceRect, 1); // Optional outline if WithOutline then begin lineW := Max(1.0, s * 0.04); Canvas.Stroke.Kind := TBrushKind.Solid; Canvas.Stroke.Color := LineColor; Canvas.Stroke.Thickness := lineW; Canvas.Stroke.Cap := TStrokeCap.Round; Canvas.Stroke.Join := TStrokeJoin.Round; Canvas.DrawEllipse(faceRect, 1); end; // Eyes only (no mouth) eyeR := s * 0.08; eyeOffsetX := s * 0.20; eyeOffsetY := -s * 0.10; eyeRectL := RectF( c.X - eyeOffsetX - eyeR, c.Y + eyeOffsetY - eyeR, c.X - eyeOffsetX + eyeR, c.Y + eyeOffsetY + eyeR ); eyeRectR := RectF( c.X + eyeOffsetX - eyeR, c.Y + eyeOffsetY - eyeR, c.X + eyeOffsetX + eyeR, c.Y + eyeOffsetY + eyeR ); Canvas.Fill.Color := EyeColor; Canvas.FillEllipse(eyeRectL, 1); Canvas.FillEllipse(eyeRectR, 1); // Mouth (smile) var mouthW, mouthH, mouthOffsetY, mouthLineW: Single; var mouthRect: TRectF; mouthW := s * 0.5; mouthH := s * 0.28; mouthOffsetY := s * 0.18; mouthRect := RectF( c.X - mouthW * 0.5, c.Y + mouthOffsetY - mouthH * 0.5, c.X + mouthW * 0.5, c.Y + mouthOffsetY + mouthH * 0.5 ); mouthLineW := Max(1.0, s * 0.04); Canvas.Stroke.Kind := TBrushKind.Solid; Canvas.Stroke.Color := EyeColor; Canvas.Stroke.Thickness := mouthLineW; Canvas.Stroke.Cap := TStrokeCap.Round; Canvas.Stroke.Join := TStrokeJoin.Round; var points: TArray<TPointF>; var i, segments: Integer; var startAngleDeg, sweepDeg, angleRad, cx, cy, rx, ry: Single; startAngleDeg := 200.0; sweepDeg := 140.0; segments := 24; SetLength(points, segments + 1); cx := (mouthRect.Left + mouthRect.Right) * 0.5; cy := (mouthRect.Top + mouthRect.Bottom) * 0.5; rx := mouthRect.Width * 0.5; ry := mouthRect.Height * 0.5; for i := 0 to segments do begin angleRad := DegToRad(startAngleDeg + sweepDeg * (i / segments)); points[i] := PointF(cx + Cos(angleRad) * rx, cy - Sin(angleRad) * ry); end; for i := 0 to segments - 1 do Canvas.DrawLine(points[i], points[i + 1], 1); end; end.
扩展功能(可选)
如果需要点击按钮切换笑脸的显示/隐藏状态,可修改按钮事件代码:
procedure TForm1.btnPaintSmileClick(Sender: TObject); begin FDrawSmile := not FDrawSmile; // 切换标记状态 PaintBox1.Repaint(); end;
内容的提问来源于stack exchange,提问作者user31362432
相关产品推荐
相关产品推荐

