PAYATHON 2026

如何在 Delphi FMX 中通过代码动态修改 TButton 的背景颜色?

数据阿明

可以保留现有的 TButton,通过修改样式中的背景对象,在运行时设置颜色。对于数据库提供任意颜色的需求,可以为按钮准备包含 TRectangle 背景的自定义样式,再通过代码修改该背景的 Fill.Color。

原代码生效有两个前提:'background' 对应的是 TRectangle,并且该对象负责绘制按钮背景。题目没有提供目标平台和实际样式结构,暂时无法确定具体是哪个条件不满足。

为什么原代码没有效果?

FindStyleResource('background') 按样式资源名称查找对象,返回的对象不一定是矩形;找不到时会返回 nil。具体行为见 FindStyleResource 文档。

可以在原代码的 if 处打断点,检查以下情况:

  • Obj = nil:当前样式中没有找到该资源,或者调用时样式尚未准备好。
  • Obj <> nil,但不是 TRectangle:类型判断不成立,赋值语句不会执行。
  • Obj is TRectangle:继续检查它的 Fill.Kind、是否被其他对象遮挡,以及是否有动画覆盖颜色。

按钮背景也可能由位图样式对象绘制。例如,Embarcadero 的按钮样式示例通过 NormalLink、HotLink、PressedLink 等属性指定各状态的背景图。这类背景不能直接当作 TRectangle 修改 Fill.Color。

为现有 TButton 建立可改色的样式

只需修改按钮使用的样式,窗体上的组件仍然是 TButton。

  1. 右键单击一个按钮,选择 Edit Custom Style,复制当前样式进行编辑。
  2. 将该自定义样式根对象的 StyleName 设为明确的名称,例如 runtimecolorbuttonstyle。
  3. 在样式中找到负责绘制背景的对象。如果它不是矩形,将该背景绘制部分换成 TRectangle,并将矩形的 StyleName 设为 background。
  4. 将矩形的 Align 设为 Contents、HitTest 设为 False、Fill.Kind 设为 Solid,并使其位于文字后方。
  5. 保留文字、布局以及需要的焦点和交互效果。检查是否有动画修改 Fill.Color,或有其他状态背景遮住矩形。
  6. 保存样式,确认窗体使用了对应的 TStyleBook,并让需要改色的按钮使用 StyleLookup = 'runtimecolorbuttonstyle'。

这些按钮可以共用同一个样式定义,无须为每种数据库颜色建立一份样式。Edit Custom Style 和 TStyleBook 的基本用法见自定义 FireMonkey 样式文档。

在运行时设置颜色,并在样式重新应用后恢复

下面以 Button1 为例,前提是已经完成上述自定义样式设置。

在窗体单元的 uses 中包含所需单元,已有的引用保留即可:

uses
  System.SysUtils,
  System.UITypes,
  FMX.Types,
  FMX.Objects,
  FMX.Graphics;

在窗体类的 private 区域增加:

private
  FButton1Color: TAlphaColor;
  procedure ApplyButton1Color;
  procedure SetButton1Color(const AColor: TAlphaColor);

通过对象检查器为 Button1.OnApplyStyleLookup 创建名为 Button1ApplyStyleLookup 的事件处理方法,再添加以下实现:

procedure TForm1.ApplyButton1Color;
var
  Obj: TFmxObject;
begin
  Obj := Button1.FindStyleResource('background');

  if Obj = nil then
    raise EInvalidOperation.Create(
      'Button1: background style resource not found');

  if not (Obj is TRectangle) then
    raise EInvalidOperation.CreateFmt(
      'Button1: background is %s, expected TRectangle',
      [Obj.ClassName]);

  TRectangle(Obj).Fill.Kind := TBrushKind.Solid;
  TRectangle(Obj).Fill.Color := FButton1Color;
end;

procedure TForm1.SetButton1Color(const AColor: TAlphaColor);
begin
  FButton1Color := AColor;
  Button1.ApplyStyleLookup;
  ApplyButton1Color;
end;

procedure TForm1.Button1ApplyStyleLookup(Sender: TObject);
begin
  ApplyButton1Color;
end;

将 TForm1 替换为实际窗体类名。在窗体初始化逻辑中设置初始颜色,例如:

SetButton1Color(TAlphaColorRec.Red);

读取数据库颜色并转换为 TAlphaColor 后,也调用 SetButton1Color,更新保存的颜色和界面。数据库字段的编码格式没有给出,因此这里不假设具体的转换方法。

ApplyStyleLookup 用于获取并应用样式;OnApplyStyleLookup 在样式应用时触发,适合重新设置实例的颜色。事件处理方法中只修改背景,不再调用 ApplyStyleLookup。相关行为可参见 ApplyStyleLookup 文档和 OnApplyStyleLookup 文档。

样式结构不符合约定时,这段示例会明确报错,便于发现配置问题。多个按钮使用时,可以把背景赋色逻辑提取成公共方法,并分别保存每个按钮的目标颜色。不要长期缓存样式背景对象的引用,重新应用样式后应重新查找。

TintColor 能否直接使用?

如果目标平台和当前样式支持着色,可以先尝试更简单的方式:

Button1.TintColor := TAlphaColorRec.Red;

不过,TintColor 依赖样式支持。官方文档明确说明部分按钮不支持着色,并注明其适用范围为移动平台 iOS 和 Android。因此,它不能作为所有平台、所有按钮样式都有效的背景色接口。详见 TintColor 文档。

如果要求将任意数据库颜色显示为确定的纯色背景,自定义矩形背景更容易控制。还需检查悬停、按下、禁用和焦点状态,避免状态切换后覆盖颜色,并确认按钮文字仍然清晰可读。

如果数据库颜色采用 $AARRGGBB 格式,应注意 Alpha 通道:例如不透明红色是 $FFFF0000,$00FF0000 的 Alpha 为零。若数据库采用其他格式,需要先按其真实格式转换,不能直接把所有颜色整数都当作 TAlphaColor 使用。

备注:内容仅供参考。