影评周公子 2025-09-19 21:25 采纳率: 98.8%
浏览 2
已采纳

RadStudio 12.3中FMX高DPI显示异常如何解决?

在使用 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 设置不一致时,界面控件如 TButtonTEdit 等常出现模糊、布局错位或字体渲染失真现象。

    根本原因在于: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 意识级别。

    1. 右键项目 → “Options” → “Application” → “Appearance”
    2. 设置 HDPI SettingTrue
    3. 确认勾选 “Enable HDPI awareness”
    4. 在 “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 一致性。以下为关键代码段示例:

    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;
    graph TD A[启动应用] --> B{是否启用PerMonitorV2?} B -- 是 --> C[系统通知WM_DPICHANGED] B -- 否 --> D[使用DPI虚拟化渲染] C --> E[接收新DPI值与建议窗口矩形] E --> F[调用SetBounds调整位置尺寸] F --> G[遍历控件并缩放Size与Font] G --> H[重绘界面保持清晰]

    四、高级优化与最佳实践建议

    为进一步提升多 DPI 环境下的稳定性,建议采取以下措施:

    • 使用矢量图形资源(SVG)替代位图图标,避免缩放失真
    • 避免硬编码坐标和尺寸,改用 Anchors 或 Layout 组件进行相对布局
    • OnResize 事件中重新计算关键控件比例
    • 测试场景覆盖:100%, 150%, 200%, 250% 缩放级别及跨屏拖拽
    • 利用 IDeviceInfo 接口获取当前屏幕 DPI 信息
    • 禁用第三方组件中的非 HDPI 兼容绘制逻辑
    • 发布前使用 Microsoft's WinSpyDPI Analyzer 验证感知模式
    • 考虑封装通用 DPI 适配单元,供多个项目复用
    • 监控 Embarcadero 官方补丁,如 QC#XXX 对 FMX.HDPI 的改进
    • 文档化团队内部的 HDPI 开发规范,统一设置流程
    本回答被题主选为最佳回答 , 对您是否有帮助呢?
    评论

报告相同问题?

问题事件

  • 已采纳回答 10月23日
  • 创建了问题 9月19日