如何在 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。
- 右键单击一个按钮,选择 Edit Custom Style,复制当前样式进行编辑。
- 将该自定义样式根对象的
StyleName设为明确的名称,例如runtimecolorbuttonstyle。 - 在样式中找到负责绘制背景的对象。如果它不是矩形,将该背景绘制部分换成
TRectangle,并将矩形的StyleName设为background。 - 将矩形的
Align设为Contents、HitTest设为False、Fill.Kind设为Solid,并使其位于文字后方。 - 保留文字、布局以及需要的焦点和交互效果。检查是否有动画修改
Fill.Color,或有其他状态背景遮住矩形。 - 保存样式,确认窗体使用了对应的
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 使用。
备注:内容仅供参考。
