在使用 RAD Studio 12.3 开发 FMX 应用程序时,高 DPI 显示异常是一个常见问题:当应用程序运行在 4K 或高分辨率屏幕上时,界面元素(如按钮、文本框)出现模糊、布局错位或字体渲染失真。此问题源于 FMX 框架对 Windows 高 DPI 设置的默认缩放处理不完善,尤其是在未正确启用 Per-Monitor V2 DPI 感知模式的情况下。即使设置了 Form 的 Scaled 属性为 True,控件仍可能未按预期缩放。如何在 RAD Studio 12.3 中通过配置项目清单文件、调整 HDPI 属性及代码级适配,实现 FMX 应用在多 DPI 显示器下的清晰与布局一致性?
1条回答 默认 最新
巨乘佛教 2025-10-22 04:27关注一、高 DPI 显示问题的背景与成因分析
在使用 RAD Studio 12.3 开发跨平台 FMX(FireMonkey)应用程序时,高 DPI 显示适配已成为影响用户体验的关键技术瓶颈。尤其当应用部署于 4K 或多显示器环境中,主屏与副屏 DPI 设置不一致时,界面控件如
TButton、TEdit等常出现模糊、布局错位或字体渲染失真现象。根本原因在于:FMX 框架默认未启用 Windows 的 Per-Monitor V2 DPI Awareness 模式,导致系统采用“DPI 虚拟化”机制进行缩放,即以 96 DPI 渲染后由桌面窗口管理器拉伸显示,造成图像模糊。此外,即使将窗体的
Scaled = True,FMX 的缩放逻辑仍可能未正确响应运行时 DPI 变化,特别是在跨屏拖动窗口时。问题类型 表现形式 触发场景 图像模糊 图标、文本边缘锯齿或发虚 DPI 缩放 > 100% 且未启用 DPI 感知 布局错位 控件重叠、对齐偏移 窗体在不同 DPI 屏幕间移动 字体失真 文字过小或过大,行距异常 Font.Size 未按 DPI 动态调整 事件坐标偏差 点击位置与响应区域不匹配 未处理高 DPI 下的坐标转换 二、项目级配置:清单文件与 DPI 感知模式设置
解决高 DPI 问题的第一步是确保应用程序具备正确的 DPI 感知能力。Windows 要求通过嵌入或外部 manifest 文件 声明 DPI 意识级别。
- 右键项目 → “Options” → “Application” → “Appearance”
- 设置
HDPI Setting为True - 确认勾选 “Enable HDPI awareness”
- 在 “Custom Manifest” 中选择或嵌入支持 Per-Monitor V2 的 manifest 文件
以下是推荐使用的自定义清单片段(保存为
app.manifest并关联到项目):<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1"> <application xmlns="urn:schemas-microsoft-com:asm.v3"> <windowsSettings> <dpiAwareness xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">PerMonitorV2</dpiAwareness> <dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">True</dpiAware> </windowsSettings> </application> </assembly>该配置优先启用
PerMonitorV2,兼容旧系统回退至传统 DPI 感知。RAD Studio 12.3 支持此格式,但需手动替换默认生成的 manifest。三、代码层适配策略与运行时处理
即便配置了 manifest,FMX 在动态 DPI 切换时仍需代码干预以保证 UI 一致性。以下为关键代码段示例:
graph TD A[启动应用] --> B{是否启用PerMonitorV2?} B -- 是 --> C[系统通知WM_DPICHANGED] B -- 否 --> D[使用DPI虚拟化渲染] C --> E[接收新DPI值与建议窗口矩形] E --> F[调用SetBounds调整位置尺寸] F --> G[遍历控件并缩放Size与Font] G --> H[重绘界面保持清晰]procedure TForm1.FormCreate(Sender: TObject); begin // 强制启用高 DPI 缩放 if Assigned(Screen) then ScaleFactor := Screen.ScreenScale; end; procedure TForm1.WM_DPICHANGED(var Msg: TWM_DPICHANGED); var NewDPI: Integer; Rect: TRect; begin NewDPI := Msg.DPI; Rect := Msg.Rect; // 调整窗体尺寸以适应新 DPI SetBounds(Rect.Left, Rect.Top, Rect.Width, Rect.Height); // 手动触发控件重绘与字体缩放 ScaleControls(NewDPI / 96); // 相对于 96 DPI 的缩放因子 Invalidate; end; procedure TForm1.ScaleControls(const Scale: Single); var i: Integer; Ctrl: TFmxObject; begin for i := 0 to ChildCount - 1 do begin Ctrl := Children[i]; if Ctrl is TControl then begin TControl(Ctrl).Size.Scale(Scale); if Assigned(TControl(Ctrl).FindStyleResource('text')) then TControl(Ctrl).Font.Size := TControl(Ctrl).Font.Size * Scale; end; end; end;四、高级优化与最佳实践建议
为进一步提升多 DPI 环境下的稳定性,建议采取以下措施:
- 使用矢量图形资源(SVG)替代位图图标,避免缩放失真
- 避免硬编码坐标和尺寸,改用 Anchors 或 Layout 组件进行相对布局
- 在
OnResize事件中重新计算关键控件比例 - 测试场景覆盖:100%, 150%, 200%, 250% 缩放级别及跨屏拖拽
- 利用
IDeviceInfo接口获取当前屏幕 DPI 信息 - 禁用第三方组件中的非 HDPI 兼容绘制逻辑
- 发布前使用 Microsoft's
WinSpy或DPI Analyzer验证感知模式 - 考虑封装通用 DPI 适配单元,供多个项目复用
- 监控 Embarcadero 官方补丁,如 QC#XXX 对 FMX.HDPI 的改进
- 文档化团队内部的 HDPI 开发规范,统一设置流程
本回答被题主选为最佳回答 , 对您是否有帮助呢?解决 无用评论 打赏 举报