HarmonyOS 7 InsightBoard 多形态适配实录 04:ArkUI × Navigation × Window:平板、PC 与自由窗口的信息密度重排【鸿蒙心迹】
03 把 InsightBoard 的悬停态处理完以后,我把同一套工程放到平板和 PC / 2in1 上重新看了一遍。
这次最明显的问题不是组件错位,而是页面太像手机了。
窗口已经 1180vp 宽,左侧还保留手机风格的底部导航,中间列表只占一小块,详情区域留出大面积空白。功能都能用,但大屏真正增加的空间并没有换来更高的信息效率。
所以 04 不再增加新的 Fold API,而是处理大屏下的三件事:
导航层级
列表密度
自由窗口缩放
本轮固定数据:
taskId: adapt_20261002_04
device: PC / 2in1
window: 1440 × 960 px
viewport: 1180 × 786 vp
mode: EXPANDED
navigation: RAIL
density: DENSE
railWidth: 72vp
listWidth: 320vp
detailMinWidth: 480vp
gutter: 24vp
visibleItems: 9
selectedItem: insight_042
filter: 全部
resize:
960vp → 1180vp
status: READY

一、大屏不是把 COMPACT 页面撑开
InsightBoard 的第一版 EXPANDED 很简单:MEDIUM 双栏再加一个 NavigationRail。
结构没错,问题出在信息密度。
列表卡片仍然使用手机上的大图、标题两行、摘要两行和很大的上下间距。1180vp 宽时一屏只能看到 5 条。
右侧详情也保留手机大 Padding,结果就是“看起来很宽,但能看到的信息没多多少”。
我重新定义大屏目标:
导航常驻
列表更紧凑
详情更宽
辅助信息直接展示
减少无意义页面跳转
HarmonyOS 的多设备适配指南本身也强调,要根据屏幕尺寸和使用场景重新规划信息层级、导航结构和关键操作区域,而不是简单放大。对 InsightBoard 来说,大屏的价值应该是“少跳一层、同屏多看一些”,而不是“同一张卡片变得更宽”。
二、Navigation 模式要随空间变化,但页面栈不能重建
大屏里 InsightBoard 使用 Rail。
当前结构:
Navigation Rail
→ Insight List
→ Detail
自由窗口缩窄到 MEDIUM 后:
List
→ Detail
继续缩到 COMPACT:
Stack
这意味着同一个 insight_042 可能经历“三栏 → 双栏 → 单页栈”。
如果每次都新建一个 NavigationController,业务状态一定会跳。
04 新增 AdaptiveNavigationRouter.ets:
export enum NavigationLayout {
STACK = 'STACK',
SPLIT = 'SPLIT',
RAIL = 'RAIL'
}
export class AdaptiveNavigationRouter {
resolve(
mode: LayoutMode
): NavigationLayout {
if (mode === LayoutMode.COMPACT) {
return NavigationLayout.STACK
}
if (mode === LayoutMode.MEDIUM) {
return NavigationLayout.SPLIT
}
return NavigationLayout.RAIL
}
}
Router 只返回结构模式。
selectedItemId / filter / scrollOffset / detailTab 都继续保存在 Store。
这一点和 01、02 的连续性设计是同一条主线:布局可以重排,业务上下文不能被布局重排顺手重置。
三、EXPANDED 里新增的是信息,而不是装饰
这次我给三栏定了明确职责。
1. Rail:72vp
只放一级入口:
首页
洞察
分析
收藏
设置
不放当前文章标题,不放业务筛选。
2. List:320vp
列表改成紧凑卡片:
小缩略图
一行标题
时间
一枚状态
本轮同屏可见从 5 条提高到 9 条。
3. Detail:最小 480vp
详情区域把原来需要点第二层页面才能看到的信息直接展开:
标签
进度
来源
更新时间
相关指标
这个变化才是大屏的价值。
不是“同一张手机卡片变宽”,而是少一次跳转。
四、信息密度本身也应该是一个策略
之前页面里直接写:
.padding(24)
.fontSize(18)
PC 版改成:
.padding(16)
.fontSize(16)
几轮以后组件里全是设备判断。
最后我增加 DensityPreset:
export enum DensityPreset {
COMPACT = 'COMPACT',
COMFORTABLE = 'COMFORTABLE',
DENSE = 'DENSE'
}
export interface DensityTokens {
pagePadding: number
cardGap: number
listItemHeight: number
titleSize: number
}
export function resolveDensity(
mode: LayoutMode
): DensityTokens {
if (mode === LayoutMode.EXPANDED) {
return {
pagePadding: 20,
cardGap: 10,
listItemHeight: 72,
titleSize: 16
}
}
if (mode === LayoutMode.MEDIUM) {
return {
pagePadding: 20,
cardGap: 14,
listItemHeight: 84,
titleSize: 17
}
}
return {
pagePadding: 16,
cardGap: 16,
listItemHeight: 96,
titleSize: 18
}
}
这段代码解决的是“适配参数散落在几十个组件里”的问题。
后面如果 PC 想再提高密度,只改策略,不需要满项目搜索 padding(16)。
五、自由窗口场景里,设备是 PC,但页面可能重新变成 MEDIUM
这一篇特意没有写:
PC = EXPANDED
因为自由窗口是最能暴露这个错误的场景。
PC 上把窗口从 1180vp 缩到 780vp:
device 还是 PC
mode 已经变成 MEDIUM
此时 Rail 应该收起。
HarmonyOS 多窗口布局适配文档明确指出,进入悬浮窗或分屏后,窗口尺寸和宽高比会变化,应用应根据窗口尺寸更新布局,并可通过 windowSizeChange 监听变化。
所以 04 没有增加新的“PC 判断”,还是继续使用最开始的 BreakpointPolicy。
这种连续性比多写一个设备枚举更重要。
六、我给自由窗口加了最小宽度,但不把它当断点替代品
PC 自由窗口缩得太小时,三栏已经没有意义。
当前 InsightBoard 的工程下限:
完整三栏:
建议 >= 960vp
MEDIUM 双栏:
600~839vp
COMPACT:
<600vp
注意这里的 840~959vp。
虽然 BreakpointPolicy 已经进入 EXPANDED,但大屏三栏还不一定展开到完整 Rail。
我最终增加一个 LargeScreenShell 内部策略:
private resolveRailVisible(
widthVp: number
): boolean {
return widthVp >= 960
}
也就是说:
840~959vp
= EXPANDED content density
+ no permanent rail
>=960vp
= full three-pane large shell
这样断点仍然代表整体布局级别,Shell 内部还可以做更细的组件显隐。
不会为了一个 Rail 再新增第四档 Breakpoint。
七、窗口变宽时,不应该把列表拉成无限宽
这次还有一个很明显的视觉问题。
窗口从 960vp 拉到 1180vp 后,如果列表按 30% 比例增长,列表会越来越宽,详情反而没有得到足够空间。
所以 04 改成固定 / 约束组合:
rail = 72vp
list = 320vp
gutter = 24vp
detail = remaining
detail min = 480vp
大屏继续变宽时,主要增长详情区域。
列表里的标题长度本来就有限,没有必要跟着窗口无限扩张。
这种“固定局部 + 弹性主内容”的方式,比三个区域都按百分比更适合 InsightBoard。
八、同一条 selectedItem 必须在三种导航结构里有同一语义
这次我又遇到一个比布局更细的问题。
COMPACT 里 selectedItemId 表示当前 pushed 详情页;MEDIUM 里它表示右侧详情;EXPANDED 里它又表示列表选中项和详情源。
如果三个组件各自维护状态,看起来都是 insight_042,实际来源却不同。
最后我把“选中洞察”收口成 Store 的唯一值:
export class InsightSelectionStore {
private selectedItemId: string =
'insight_042'
get(): string {
return this.selectedItemId
}
select(id: string): void {
this.selectedItemId = id
}
}
Navigation 只负责“怎么显示”,不再拥有选中值。
这样自由窗口在 1180vp → 780vp → 520vp 来回缩放时,结构变三次,选中项始终不变。
九、筛选条件也不能因为列表组件换了就丢
04 里固定筛选:
filter = 全部
这看起来不起眼,但它是另一个状态连续性检查点。
DENSE 列表和 COMFORTABLE 列表不是同一个组件。如果 filter 被写在 ListPane 自己的 @State 里,结构替换时就会回默认。
现在筛选、排序和 selectedItem 都在业务 Store。
布局只消费:
filter
sort
selectedItem
这一点后面 05 做多窗口恢复时会继续用到。
十、DevEco 图里最重要的是 960 → 1180,而不是设备名
最终开发图:

HiLog:
taskId=adapt_20261002_04
windowSizeChange
960vp → 1180vp
mode=EXPANDED
navigation=RAIL
density=DENSE
visibleItems=9
selectedItem preserved
insight_042
filter preserved
全部
status=READY
这里我没有写“device=PC,所以 mode=EXPANDED”。
相反,日志始终保留 viewport。
只要窗口缩回 780vp,即使设备字段还是 PC,Navigation 也会回到 SPLIT。
十一、大屏适配还要检查键鼠和窗口状态,但这一篇先不扩题
PC / 2in1 真正产品化还会继续遇到:
键盘焦点
鼠标 Hover
右键
拖拽
快捷键
子窗口
独立子窗
窗口层级
HarmonyOS 7 API 26 的子窗口能力里也出现了自由窗口下独立子窗相关能力。
但这些没有塞进 04。
这一篇的目标仍然是“同一个主窗口如何根据空间重排导航和信息密度”。
如果把键鼠、子窗全部混进来,文章会失去主线。
后面 05 会处理多窗口和页面栈恢复,届时再把窗口生命周期放进来更合适。
十二、运行结果:更大的窗口,终于带来了更高的信息效率
最终运行图:

统一数据:
taskId: adapt_20261002_04
device: PC / 2in1
window: 1440 × 960 px
viewport: 1180 × 786 vp
mode: EXPANDED
navigation: RAIL
density: DENSE
railWidth: 72vp
listWidth: 320vp
detailMin: 480vp
visibleItems: 9
selectedItem: insight_042
filter: 全部
status: READY
这一步完成以后,InsightBoard 已经覆盖了四种明显不同的布局阶段:
COMPACT 手机
MEDIUM 折叠屏展开
HOVER 半折叠
EXPANDED 平板 / PC
更重要的是,它们并不是四套工程。
仍然是同一个 Store、同一条 selectedItem、同一个 BreakpointPolicy,以及按场景拆出来的 Layout Coordinator。
下一轮会继续 05、06。
05 会处理真正的多窗口生命周期:主窗口缩放、页面栈、详情页、窗口退出再回来时业务任务怎么保持。
06 最后做全系列回归:手机、折叠屏、悬停、平板、PC / 2in1 一起跑,检查布局抖动、重复监听、状态丢失和资源释放。
做到 06 后,这个系列会按原计划正式结束,不继续无限追加。
参考资料
- HarmonyOS 多设备通用适配指南:https://developer.huawei.com/consumer/cn/multidevice/adaptive-apps/
- 多窗口布局适配:https://developer.huawei.com/consumer/en/doc/harmonyos-guides-V14/multi-window-layout-adapt-V14
- 子窗口开发指导:https://developer.huawei.com/consumer/cn/doc/HarmonyOS-Guides/subwindow-guide
更多推荐



所有评论(0)