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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.12 10:44:55