openharmony源码编译之窗口管理屏幕适配

OpenHarmony 窗口管理屏幕适配操作教程

以 RK3588 + MIPI DSI 2560×1600 屏幕为案例

零、前置条件

需完成底软驱动屏幕适配,开机点亮屏幕

一、背景

OpenHarmony 窗口管理服务(WMS)内置了一个浮动窗口最大尺寸限制,默认值针对 1080p 等常见分辨率设计。当设备接入更高分辨率的屏幕(如 2560×1600)时,浮动类型的系统窗口(弹窗、输入法、对话框等)会被裁剪,导致无法铺满屏幕。

本文档说明适配过程中涉及的关键参数、计算方法和具体修改方案。

二、关键概念

2.1 三个核心参数

参数 含义 单位
物理分辨率 屏幕实际的像素数量 px(物理像素)
dpi 每英寸像素密度,在设备配置文件中人为设定 dots per inch
vpr 虚拟像素比(Virtual Pixel Ratio),由 dpi 计算得出 无量纲
dp 密度无关像素(Density-independent Pixel),逻辑像素 dp

2.2 参数之间的换算关系

复制代码
vpr = dpi ÷ 160

dp = px ÷ vpr

px = dp × vpr

其中 160 是 OHOS 系统中硬编码的基准密度常量,定义在 window_manager 的头文件中。

2.3 基准密度 160 的代码出处

160 这个数字不是经验值,而是在系统源码中以常量形式定义的。

定义位置window_manager/interfaces/innerkits/dm/dm_common.h

cpp 复制代码
// 第 32 行
constexpr int DOT_PER_INCH = 160;

// 第 36 行
constexpr uint32_t BASELINE_DENSITY = 160;

使用位置window_manager/dmserver/src/abstract_display.cpp

系统在初始化 Display 时,读取 display_manager_config.xml 中配置的 dpi 值,然后除以 BASELINE_DENSITY(即 160)得到 virtualPixelRatio_(即 vpr):

cpp 复制代码
// abstract_display.cpp 第 78~81 行
uint32_t densityDpi = static_cast<uint32_t>(numbersConfig["dpi"][0]);
if (densityDpi >= DOT_PER_INCH_MINIMUM_VALUE && densityDpi <= DOT_PER_INCH_MAXIMUM_VALUE) {
    virtualPixelRatio_ = static_cast<float>(densityDpi) / BASELINE_DENSITY;
    absScreen->SetVirtualPixelRatio(virtualPixelRatio_);

同样的逻辑在 window_scene/session_manager/src/screen_session_manager.cpp 第 443 行也有:

cpp 复制代码
densityDpi_ = static_cast<float>(densityDpi) / BASELINE_DENSITY;

完整的调用链

复制代码
display_manager_config.xml 中 <dpi>200</dpi>
    ↓ 系统启动时读取
abstract_display.cpp / screen_session_manager.cpp
    ↓ densityDpi / BASELINE_DENSITY
virtualPixelRatio_ = 200 / 160 = 1.25
    ↓ SetVirtualPixelRatio()
存入 Screen/Display 对象,后续所有窗口布局使用

因此 vpr = dpi ÷ 160 这个公式的 160 来自 dm_common.h 中的 BASELINE_DENSITY = 160

三、查找设备的 dpi 配置

3.1 配置文件位置

dpi 配置在产品级别的 display_manager_config.xml 中:

复制代码
vendor/<厂商>/<产品名>/window_config/display_manager_config.xml

本案例的路径:

复制代码
vendor/kaihong/633_rk3588j_isdt-2/window_config/display_manager_config.xml

3.2 配置内容

xml 复制代码
<Configs>
   <!--Window display dpi, valid range is 80~640, use 0 if no configuration is required-->
   <dpi>200</dpi>
</Configs>

当前设备的 dpi = 200。

3.3 dpi 配置的生效路径

这个 xml 文件在编译时会被打包到设备的 /system/etc/window/resources/ 目录下。系统启动时,AbstractDisplayScreenSessionManager 读取此配置,按照 2.3 节的逻辑计算出 vpr 并设置到 Screen/Display 对象中。

后续 WMS 中所有涉及 vpr 的计算(包括 GetSystemSizeLimits() 中的 maxFloatingWindowSize_ * vpr)都使用这个值。

四、计算适配参数

已知信息:

  • 屏幕物理分辨率:2560 × 1600 px
  • dpi 配置:200 (来自 display_manager_config.xml

第 1 步:计算 vpr

根据 dm_common.hBASELINE_DENSITY = 160abstract_display.cpp 中的计算逻辑:

复制代码
vpr = dpi ÷ BASELINE_DENSITY = 200 ÷ 160 = 1.25

第 2 步:计算屏幕的 dp 尺寸

复制代码
宽度 dp = 2560px ÷ 1.25 = 2048 dp
高度 dp = 1600px ÷ 1.25 = 1280 dp

第 3 步:计算原始浮动窗口最大尺寸(修改前)

WMS 中 maxFloatingWindowSize_ 默认值为 1920(单位 dp)。

复制代码
原始 maxWidth = 1920dp × 1.25 = 2400 px

2400px < 屏幕宽度 2560px,差了 160px,所以浮动窗口会被裁剪。

第 4 步:确定新的 maxFloatingWindowSize_ 值

要让浮动窗口能铺满屏幕宽度:

复制代码
maxFloatingWindowSize_ ≥ 屏幕宽度(px) ÷ vpr = 2560 ÷ 1.25 = 2048 dp

设为 2048 即可。验算:

复制代码
修改后 maxWidth = 2048dp × 1.25 = 2560 px = 屏幕宽度 ✓

计算速查公式

对于任意屏幕,只需要知道物理分辨率和 dpi,一步到位:

复制代码
maxFloatingWindowSize_ = 屏幕宽度(px) ÷ (dpi ÷ 160)

五、修改方案

需要修改 2 个文件,共 2 处核心改动 + 1 处系统栏宽度适配。

5.1 修改浮动窗口尺寸上限

文件wmserver/src/window_layout_policy.cpp

位置maxFloatingWindowSize_ 的定义处(约第 35 行)

修改内容

cpp 复制代码
// 修改前(默认值,适用于 1080p 等屏幕)
uint32_t WindowLayoutPolicy::maxFloatingWindowSize_ = 1920;

// 修改后(适用于 2560x1600@dpi200 屏幕)
uint32_t WindowLayoutPolicy::maxFloatingWindowSize_ = 2048;

作用 :此值在 GetSystemSizeLimits() 函数中被使用:

cpp 复制代码
// wmserver/src/window_layout_policy.cpp 约第 475 行
systemLimits.maxWidth_ = static_cast<uint32_t>(maxFloatingWindowSize_ * vpr);
systemLimits.maxHeight_ = static_cast<uint32_t>(maxFloatingWindowSize_ * vpr);

修改后,所有浮动窗口(Dialog、Float、SystemAlarm、Screenshot 等)的最大宽度从 2400px 提升到 2560px,不再被裁剪。

文件wmserver/src/window_layout_policy.cpp

位置FixWindowRectWithinDisplay() 函数中(约第 795 行)

StatusBar、NavigationBar、InputMethodFloat 是系统级窗口,它们的宽度由 SystemUI 应用请求决定。SystemUI 可能按照默认屏幕宽度(如 720dp → 900px)来请求,不会自动适配 2560 宽屏。因此需要在 WMS 端针对目标分辨率强制修正宽度。

修改内容

FixWindowRectWithinDisplay() 中增加目标屏幕判断和系统栏宽度修正:

cpp 复制代码
void WindowLayoutPolicy::FixWindowRectWithinDisplay(const sptr<WindowNode>& node) const
{
    // ... 原有逻辑 ...
    Rect rect = node->GetRequestRect();

    // 判断是否为目标屏幕
    bool isTargetDisplay = (displayRect.width_ == 2560 && displayRect.height_ == 1600) ||
        (displayRect.width_ == 1600 && displayRect.height_ == 2560);

    switch (type) {
        case WindowType::WINDOW_TYPE_STATUS_BAR:
            rect.posY_ = displayRect.posY_;
            if (isTargetDisplay) {
                rect.posX_ = displayRect.posX_;
                rect.width_ = displayRect.width_;  // 强制状态栏铺满屏幕宽度
            }
            break;
        case WindowType::WINDOW_TYPE_NAVIGATION_BAR:
            rect.posY_ = static_cast<int32_t>(displayRect.height_) + displayRect.posY_ -
                static_cast<int32_t>(rect.height_);
            if (isTargetDisplay) {
                rect.posX_ = displayRect.posX_;
                rect.width_ = displayRect.width_;  // 强制导航栏铺满屏幕宽度
            }
            break;
        case WindowType::WINDOW_TYPE_INPUT_METHOD_FLOAT:
            if (isTargetDisplay) {
                rect.posX_ = displayRect.posX_;
                rect.width_ = displayRect.width_;  // 强制输入法铺满屏幕宽度
            }
            break;
        default:
            // ... 原有逻辑 ...
    }
    node->SetRequestRect(rect);
}

为什么这个修改和 5.1 是独立的?

修改 解决的问题 针对的窗口
5.1 修改 maxFloatingWindowSize_ 浮动窗口尺寸被裁剪到 2400px Dialog、Float、Alarm 等浮动窗口
5.2 修改 FixWindowRectWithinDisplay 系统栏宽度不够 2560 StatusBar、NavigationBar、InputMethod

系统栏不走浮动窗口的尺寸限制逻辑(它们不是 WINDOW_MODE_FLOATING),所以必须单独处理。

5.3 确认 Cascade 文件无需额外修改

文件wmserver/src/window_layout_policy_cascade.cpp

由于 5.1 已经从源头解决了浮动窗口尺寸上限问题,UpdateFloatingWindowSizeBySizeLimits() 函数中不需要任何额外的类型豁免逻辑。

六、编译

修改完成后,仅需编译 WMS 的 libwms 库:

bash 复制代码
./build.sh --product-name <产品名> -f foundation/window/window_manager/wmserver:libwms

本案例:

bash 复制代码
./build.sh --product-name 633_rk3588j_isdt-2 -f foundation/window/window_manager/wmserver:libwms

编译产物路径(参考):

复制代码
out/<产品名>/packages/phone/system/lib/platformsdk/libwms.z.so

libwms.z.so 推送到设备的 /system/lib/platformsdk/ 目录并重启即可生效。

七、验证

7.1 窗口尺寸验证

在设备上执行:

bash 复制代码
hidumper -s WindowManagerService -a -a

检查输出中各窗口的 Rect 信息:

复制代码
WindowName           DisplayId  WinId Type Mode ZOrd [ x    y    w    h    ]
SystemUI_StatusBar   0          3     2108 102  2    [ 0    0    2560 60   ]    ← 宽度 2560 ✓
SystemUI_NavigationB 0          2     2112 102  3    [ 0    1490 2560 110  ]    ← 宽度 2560 ✓
SystemDialog1        0          39    2106 102  1    [ 0    0    2560 1600 ]    ← 全屏 ✓

7.2 日志验证

过滤 WMS 布局日志:

bash 复制代码
hilog | grep -E "Layout|SizeLimits"

关键日志:

复制代码
target display hit, winId: 36, type: 2106, displayRect: [0, 0, 2560, 1600]
UpdateWindowSizeLimits: [Update SizeLimits] winId: 36, Width: [max:2560, min:400]
SetBounds: Name:SystemDialog1 id:36 winRect: [0, 0, 2560, 1600]

确认 max:2560(而非修改前的 max:2400)即说明修改生效。

八、适配其他分辨率的参考

屏幕分辨率 dpi vpr 屏幕宽度(dp) maxFloatingWindowSize_
1920×1080 240 1.5 1280 1920(默认值即可)
2560×1600 200 1.25 2048 2048
2560×1600 320 2.0 1280 1920(默认值即可)
1920×1200 160 1.0 1920 1920(默认值即可)
2880×1800 240 1.5 1920 1920(默认值即可)
3840×2160 320 2.0 1920 1920(默认值即可)

规律 :只有当 屏幕宽度(px) ÷ vpr > 1920 时,才需要修改 maxFloatingWindowSize_。大多数高分屏因为 dpi 也高,换算后的 dp 宽度仍在 1920 以内,不需要改。本案例之所以需要改,是因为 dpi=200 相对较低,导致 dp 宽度(2048)超过了默认上限(1920)。

九、涉及文件清单

文件 修改内容
vendor/<厂商>/<产品>/window_config/display_manager_config.xml dpi 配置(只读参考,不需要修改
wmserver/src/window_layout_policy.cpp maxFloatingWindowSize_ 值 + FixWindowRectWithinDisplay 系统栏适配
wmserver/src/window_layout_policy_cascade.cpp 确认无需额外修改
相关推荐
敲代码的鱼哇4 小时前
NFC读卡能力 支持安卓/iOS/鸿蒙 UTS插件
android·ios·harmonyos
爱吃大芒果18 小时前
从零搭建完整 HarmonyOS 应用实战教程
华为·typescript·harmonyos
richard_yuu18 小时前
鸿蒙首页实战开发|ArkTS 从零搭建治愈系首页、动态问候与功能模块
华为·harmonyos
音视频牛哥1 天前
SmartMediaKit 鸿蒙NEXT GB28181设备接入SDK
华为·harmonyos·鸿蒙gb28181·鸿蒙next gb28181·鸿蒙gb28181接入·鸿蒙接入gb28181平台·鸿蒙执法记录仪gb28181
key_3_feng1 天前
鸿蒙车规级MCU开发方案
单片机·华为·harmonyos
大雷神1 天前
HarmonyOS APP<<古今职鉴定>>开源教程第14篇:碰一碰分享:NFC 近场通信
华为·华为云·harmonyos
想你依然心痛1 天前
HarmonyOS 6(API 23)实战:基于悬浮导航、沉浸光感与HMAF的“智流工坊“——低代码可视化智能体编排平台
低代码·华为·harmonyos
richard_yuu1 天前
鸿蒙ArkUI组件化实战|公共组件封装、复用解耦与上架级UI规范落地
ui·华为·harmonyos
KKei16381 天前
Flutter for OpenHarmony 学习专注模式APP技术文章
学习·flutter·华为·harmonyos