TL;DR:多窗与分栏下应以实际内容区而非窗口总宽度驱动断点。本文用 ContainerReader 拆解测量对象、布局决策与回归检查;示例仅作接口说明,尚未完成本机 SDK 或真机编译运行。
多窗把页面挤坏了:不要把窗口宽度当成内容区宽度【鸿蒙心迹】
> 适用场景:同一页面会出现在直板机、平板、折叠屏展开态、自由窗口或带侧栏的大屏布局中;你已经做了断点,却仍偶发看到卡片被压窄、标题折成多行、栅格列数和可用空间不相称。
把多窗问题归因为“窗口变窄了”很方便,但经常不准确。一次典型事故是:应用窗口看起来已经够宽,于是页面按“大屏”分三列;可页面左边固定了一块导航栏,右边又有筛选面板,真正承载卡片的区域只剩一半。断点拿的是窗口宽度,卡片消耗的却是内容区宽度。两者一旦脱钩,布局逻辑在数字上正确、在视觉上仍然错误。
本文把这个坑拆成一条可复用的链路:先确认到底量了谁,再让断点跟随实际容器,最后把验证从“拖一下看着没问题”改成可回归的场景表。示例以 ArkUI 的ContainerReader为核心;它在当前官方文档中标注为 API 26 起可用,因此请先确认工程 SDK 与目标设备能力。文中的代码按官方接口做了静态审读,未在本机 API 26 SDK、模拟器或真机完成编译/运行验证,不能把它当作已跑通的设备结论。
先把四个宽度分开:你测的未必是组件用的
排查前先给团队统一四个词。它们听起来相近,实际在多窗里会迅速分叉:
1.屏幕宽度:设备物理显示区域的尺度。它适合讨论设备形态,通常不该直接决定页面里某一张卡片排几列。
2.应用窗口宽度:当前应用实例获得的窗口空间。分屏、自由窗口、浮层、平行视界等情况下,它不等于屏幕宽度。
3.页面容器宽度:页面顶层可使用的区域。导航、工具栏、外层内边距和分栏都可能继续消耗它。
4.目标内容区宽度:最终放置 Grid、列表或详情面板的那一块区域。决定卡片最小宽度、列数和信息密度的,往往正是这一层。
最容易出现的错误,是在第 2 层做判断、在第 4 层承受后果。比如窗口有 1100vp,左侧栏固定 260vp,内容区再扣去左右边距和间距后不足以稳定支撑三列。代码若只看到 1100vp,就会给出“三列”的正确答案;用户看到的却是三列狭长卡片、图片压扁、操作区挤在一起的错误体验。
这张图不是某台设备的截图,而是刻意抽象出的因果图。它提醒我们:左侧栏是布局的一部分,不是背景;右侧容器才是规则的输入。遇到“只有大屏/分屏才坏”的问题时,先在布局树里找出真正拥有Grid的父级,再问它的尺寸从哪里来,而不是先去调整一堆阈值。
一个很像真的失败方案:把窗口断点散落在业务组件里
很多项目最早会写出类似逻辑:在页面入口读取一个全局或窗口级宽度,把isWide传给卡片、筛选条、列表。它在单栏页面里并不总是出错,所以很容易一路保留下来。真正的问题在于它把“窗口状态”当成了“局部容器状态”。
假设页面有固定导航栏和可收起的辅助面板。用户展开面板后,内容区突然缩小,但窗口本身没有改变;若isWide没变,Grid 也不会变。反过来,用户把窗口稍微缩小,内容区却没有跨越自身的列数阈值,页面又会做一次没有必要的重排。前者造成溢出,后者造成跳动。
另一个常见补丁是给卡片追加更多if:宽屏时缩字号、压间距、隐藏副标题。它能暂时遮住拥挤,却改变了内容优先级而没有修正度量来源。半年后新