当前位置:首页>java>FireMonkey编程入门(18):使用 Image List 编辑器番外(3)ImageList图像列表更新实现机制

FireMonkey编程入门(18):使用 Image List 编辑器番外(3)ImageList图像列表更新实现机制

  • 2026-08-25 21:09:33
FireMonkey编程入门(18):使用 Image List 编辑器番外(3)ImageList图像列表更新实现机制
FireMonkey编程入门(16):使用 Image List 编辑器番外(1)图像列表的使用与自定义控件设计建议
FireMonkey编程入门(17):使用 Image List 编辑器番外(2)ImageList高级属性
本篇主内容如下:
  • 定时器刷新 ImageList机制
  • 核心组件
  • 执行流程

  • 关键关注点

本篇代码是基于FireMonkey(FMX) 框架中用于 TCustomImageList 刷新图像链接(如 TImage 控件) 的定时器机制实现。它利用了 IFMXTimerService 这个平台无关的定时器服务,在跨平台(Windows、macOS、iOS、Android 等)环境下安全地调度 UI 更新。

定时器刷新 ImageList机制

在 FireMonkey 中,TImageList 是一个集中管理图像资源的组件。其他控件(如 TImageTButton 等)可以通过设置 ImageIndex 绑定到 ImageList 中的某张图。

但当 ImageList 中的图像被动态修改(例如通过 Images[Index].Assign(...) 替换图片),绑定的控件并不会自动更新——因为 FireMonkey 默认不会监听 ImageList 内容的变化。

为了解决这个问题,TCustomImageList 引入了一个 延迟批量刷新机制

  • 当图像被修改时,标记哪些索引发生了变化;
  • 启动一个短间隔定时器(100ms)
  • 定时器触发后,统一通知所有关联控件刷新显示。

✅ 优势:避免频繁刷新(如循环修改多张图时只触发一次 UI 更新),提升性能并防止闪烁。

核心组件

//变量声明FPlatformTimer: IFMXTimerService;FTimerHandle: TFmxHandle;

1. IFMXTimerService

if not TPlatformServices.Current.SupportsPlatformService(IFMXTimerService, FPlatformTimer) then  raise EUnsupportedPlatformService.Create('IFMXTimerService');
  • FireMonkey 提供的跨平台定时器抽象接口
  • 封装了各平台原生定时器(Windows 的 SetTimer、iOS 的 NSTimer、Android 的 Handler 等)。
  • 通过TPlatformServices.Current.SupportsPlatformService(...) 获取实例。

2. FPlatformTimer: IFMXTimerService

  • 定时器服务接口引用。

3. FTimerHandle: TFmxHandle

  • 定时器句柄(类似 Windows 的 UINT_PTR),用于标识和销毁定时器。
  • 初始值设为 -1 表示“未创建”。

4. FChangedList: TDictionary<Integer, TCustomDestinationItem>

  • 记录哪些图像索引被修改过,键为 ImageIndex

5. Links: array of TImageLink

  • 存储所有绑定到该 ImageList 的控件链接(如 TImage 的内部引用)。

执行流程

步骤 1:图像被修改 → 触发“变更标记”

FChangedList.AddOrSetValue(ImageIndexSomeMarker);StartTimer// 启动刷新定时器

步骤 2:StartTimer —— 创建一次性定时器

procedure TCustomImageList.StartTimer;begin  if FTimerHandle = TFmxHandle(-1then  begin    FTimerHandle := FPlatformTimer.CreateTimer(100, TimerProc);  end;end;
  • 只创建一次:防止重复启动多个定时器。
  • 100ms 延迟:给连续修改留出合并窗口。
  • 回调函数为 TimerProc。

步骤 3:TimerProc —— 定时器触发后的处理逻辑

procedure TCustomImageList.TimerProc;type  TChangedLink = record    LinkTImageLink;    IndexInteger;  end;var  I: Integer;  TmpTCustomDestinationItem;  LChangedLinksTList<TChangedLink>;  LLinkTChangedLink;begin  StopTimer;  DoChanged;  if LinkCount > 0 then  begin    LChangedLinks :TList<TChangedLink>.Create;    try      for I :0 to LinkCount - 1 do        if (Links[I] <> niland (Links[I].IgnoreIndex or ((FChangedList <> niland          FChangedList.TryGetValue(Links[I].ImageIndexTmp))) then        begin          LLink.Link :Links[I];          LLink.Index := I;          LChangedLinks.Add(LLink);        end;      for I :0 to LChangedLinks.Count - 1 do      begin        LLink :LChangedLinks[I];        if LinkContains(LLink.LinkLLink.Indexthen          LLink.Link.Change;      end;    finally      LChangedLinks.Free;    end;    if FChangedList <> nil then      FChangedList.Clear;  end;end;

(1) 立即停止定时器

StopTimer;

→ 确保定时器只执行一次(非循环),符合“延迟刷新”语义。

(2) 触发 DoChanged

DoChanged;

→ 通知 ImageList 自身有变更。

(3) 收集需要刷新的链接

for I := 0 to LinkCount - 1 do  if (Links[I] <> nil) and      (Links[I].IgnoreIndex or       ((FChangedList <> nil) and FChangedList.TryGetValue(Links[I].ImageIndex, Tmp))) then  begin    // 加入待刷新列表  end;
  • IgnoreIndex = True:表示该链接不依赖具体索引(如自定义绘制),总是刷新。
  • 否则:检查其 ImageIndex 是否在 FChangedList 中(即是否被修改过)。

(4) 逐个调用 Link.Change

LLink.Link.Change;

→ 实际触发绑定控件(如 TImage)重绘。

(5) 清空变更记录

if FChangedList <> nil then  FChangedList.Clear;

→ 重置状态,等待下一次修改。

关键关注点

特性
说明
防抖(Debounce)
多次修改只触发一次刷新(100ms 内合并)
按需刷新
只刷新真正受影响的控件(通过 ImageIndex 匹配)
跨平台兼容
使用 IFMXTimerService,无需关心底层 OS
资源安全
定时器自动销毁,避免内存泄漏
线程安全?
⚠️ 注意:FireMonkey UI 必须在主线程操作,此定时器回调默认在主线程(各平台实现保证),所以安全

这段代码充分体现了 FireMonkey 在 UI 响应优化 与 跨平台抽象 方面的成熟设计:

“当数据发生变化时,并不立即触发界面刷新,而是延迟一小段时间,将多次变更合并后统一更新 UI。”

这种“防抖+批量更新”的机制有效避免了频繁重绘带来的性能损耗和视觉闪烁。

不过,在实际使用中仍需特别注意以下两点:

  1. 定时器精度受平台影响不同操作系统对定时器的调度策略存在差异。例如,在 Android 的节电模式下,即使设置为 100ms 的定时器,实际触发延迟可能达到 200ms 甚至 500ms。因此,对实时性要求极高的场景需谨慎依赖此类延迟刷新机制。

  2. 所有 UI 操作必须在主线程执行FireMonkey 的 UI 控件(包括 TImageList 及其关联组件)并非线程安全。切勿从非主线程直接修改 ImageList 的内容或属性——这不仅可能导致界面未更新,还可能引发崩溃或数据不一致。如需从后台线程触发变更,应通过 TThread.Synchronize 或 TThread.Queue 将操作调度回主线程。

最新文章

随机文章