【寻迹校园 HarmonyOS NEXT 实战 37】一套 ArkUI 适配四档宽度:sm、md、lg、xl 响应式 Shell 实战

本章导读:这是"寻迹校园 HarmonyOS NEXT 实战"系列第 37 篇。本文基于 Index.ets、Bottom/Top/SideNavigation、HomePage.ets 与 2026-08-12 模拟器验收记录,完整拆解 <600vp600--839vp840--1279vp>=1280vp 四档 ArkUI Shell 的导航、内容密度、状态归属和页面复用方式,同时明确"保留大屏代码"不等于当前应用市场已经支持 Tablet 或 2-in-1。

上图为原创生成的响应式架构插画,不是项目截图。四档宽度共享同一条 NavPathStack、同一组业务 Service 和同一份页面状态,只替换导航外壳与内容编排。

一、为什么不能把手机底栏直接拉到桌面

Phone 底部导航适合拇指操作,但在 1280vp 以上窗口中继续贴底,会带来三个问题:

  • 用户视线和鼠标要在页面内容与屏幕底部之间反复移动;
  • 横向空间被浪费,内容却仍保持窄单栏;
  • 列表、详情和匹配候选无法同时保留上下文。

响应式设计不是"卡片变宽",而是根据输入方式、视线范围和可用空间调整信息架构。

二、四档宽度的项目契约

当前 Design Spec 和 Index.ets 使用三条边界形成四档:

档位 宽度 导航 首页结构
sm <600vp BottomNavigation 单栏 Scroll
md 600--839vp TopNavigation 列表/详情分栏,左列较窄
lg 840--1279vp SideNavigation 常驻侧栏 + 列表/详情双栏
xl >=1280vp SideNavigation 常驻侧栏 + 34/44/22 三栏工作台

断点同时控制导航、首页分栏、内容最大宽度和辅助面板,不由每个页面自行猜测。

三、本章对应的真实文件

文件 职责
entry/src/main/ets/pages/Index.ets 根宽度、四档判断、Shell 与路由
entry/src/main/ets/components/BottomNavigation.ets Phone 底部四入口
entry/src/main/ets/components/TopNavigation.ets md 顶部导航
entry/src/main/ets/components/SideNavigation.ets lg/xl 常驻侧栏
entry/src/main/ets/pages/HomePage.ets compact、split 与 workspace 首页
entry/src/main/ets/pages/ItemDetailPage.ets 可嵌入的中栏详情
entry/src/main/ets/pages/MatchResultsPage.ets 可嵌入的右栏候选
docs/design/all-pages-design-spec-v2.md 断点与跨设备设计基准

Shell 集中在 Index,业务页面通过参数接收布局能力。

四、用 onAreaChange 获取真实布局宽度

根节点通过 onAreaChange 更新 screenWidth

ts 复制代码
.onAreaChange((oldArea: Area, newArea: Area) => {
  const width: number = Number(newArea.width);
  if (width !== this.screenWidth) this.screenWidth = width;
})

这里使用根布局区域,而不是硬编码设备型号。窗口缩放、横竖屏或多窗口改变可用宽度时,同一判断仍然成立。

五、断点判断为什么集中在 Index

Index.ets 只保留三组核心判断:

ts 复制代码
private isMediumScreen(): boolean {
  return this.screenWidth >= 600 && this.screenWidth < 840;
}

private isLargeScreen(): boolean {
  return this.screenWidth >= 840;
}

private isExtraLargeScreen(): boolean {
  return this.screenWidth >= 1280;
}

如果首页、消息、我的和发布页分别维护断点,600、840、1280 很容易出现边界差一 vp 的问题。集中判断后,页面只接收 splitViewmediumViewworkspaceView 等语义参数。

六、sm:底部导航与单任务页面

sm < 600vp 使用 BottomNavigation。首页为单栏 Scroll,点击记录通过 NavPathStack 进入详情页:

  • 内容宽度全部用于当前任务;
  • 底栏四入口高度 72vp;
  • 中央"登记"按钮使用 48×48vp 圆形操作;
  • 搜索、筛选、列表按垂直顺序组织;
  • 详情、匹配、发布和认领分别进入独立页面;
  • 返回栈语义清晰,适合单手操作。

Phone 上不强行显示左右双栏,因为每一栏都会变得过窄。

七、md:顶部导航释放底部空间

600--839vp 使用 TopNavigation。顶部导航的每项高度为 48vp,内容区下方不再被固定底栏占用。

md 档首页已经开启 splitView,左侧查询列表约 42%,右侧展示详情。左列约束为 280--340vp,避免窄窗口中把详情压成一条竖线。

顶部导航并不意味着所有页面都要双栏;表单和次级路由仍可使用受控最大宽度,在宽屏中居中展示。

八、lg:常驻侧栏建立桌面级导航

840--1279vp 使用 232vp 宽的 SideNavigation。侧栏包含:

  • 产品名和本机模式说明;
  • 首页、登记、消息、我的四个稳定入口;
  • 选中态品牌容器;
  • 每个入口的可访问名称与动作描述;
  • 底部"本机单机模式"边界提示。

内容区在侧栏右侧居中,最大宽度由 tabContent() 控制为 1120vp,避免窗口继续变宽时正文无限拉长。

九、xl:第三栏不是装饰,而是业务辅助

当宽度达到 1280vp,首页的 workspaceView 为真。此时:

  • 左栏 34%:搜索、组合筛选和本机记录;
  • 中栏 44%:当前记录或候选详情;
  • 右栏 22%:当前查询记录的 Top 3 脱敏匹配候选;
  • 点击右栏候选只更新中栏,不重置左栏;
  • 侧栏仍保持全局导航与业务边界。

这比"显示更多卡片"更符合失物匹配任务,因为用户需要同时比较查询、证据和候选。

上图展示根宽度只决定 Shell,selectedTabNavPathStack、查询条件、选中记录与 Service 数据不随布局分支重建。

十、三种导航组件为什么不能写成一个超大组件

Bottom、Top 和 Side 虽然入口相同,但信息结构不同:

  • Bottom 使用等宽四项和中央圆形登记入口;
  • Top 使用水平按钮,登记入口为主色矩形;
  • Side 使用图标+文字的纵向列表,并显示产品与模式说明。

如果强行用一个组件加大量条件分支,布局、间距和焦点顺序会互相牵连。当前项目共享 BottomTab、图标、颜色和 onSelect 契约,但保留三个清晰组件。

十一、三个 Shell 如何共享同一个路由栈

Index 只创建一个:

ts 复制代码
private readonly pathStack: NavPathStack = new NavPathStack();

Phone 的 Navigation 包裹底栏主内容;md 与 lg/xl 的 Navigation 分别位于顶部或侧栏 Shell 内,但都消费同一个 pathStackdestinationBuilder

因此窗口变化不应创建新的路由对象,已打开的发布、认领或详情路径仍有权威来源。

十二、Tab 状态为什么放在 Shell 层

selectedTab 属于全局导航状态,保存在 Index

ts 复制代码
@Local selectedTab: BottomTab = BottomTab.HOME;

Bottom、Top、Side 只接收当前值和回调。布局切换后,新导航组件仍读取同一个 selectedTab,不会从"消息"莫名跳回"首页"。

这体现了一个关键原则:状态生命周期应高于使用它的布局分支。

十三、登记入口为什么不直接改变 selectedTab

登记不是常驻 Tab,而是次级流程:

ts 复制代码
if (tab === BottomTab.PUBLISH) {
  this.pathStack.pushPathByName(AppRoute.PUBLISH_TYPE, undefined);
  return;
}
this.selectedTab = tab;

这样用户完成或取消登记后可以返回原 Tab,上层导航选中态不会被一个临时流程污染。

十四、HomePage 如何消费布局能力

Shell 下传三项参数:

ts 复制代码
HomePage({
  splitView: this.screenWidth >= 600,
  mediumView: this.screenWidth >= 600 && this.screenWidth < 840,
  workspaceView: this.screenWidth >= 1280
})

HomePage 根据能力选择 compactHome()splitScreenHome(),而不是再次读取设备信息。这让页面更容易测试,也避免平台判断散落。

十五、分栏宽度为什么还需要 min/max

左栏百分比之外还有约束:

ts 复制代码
.width(this.workspaceView ? '34%' : (this.mediumView ? '42%' : '38%'))
.constraintSize({
  minWidth: this.workspaceView ? 300 : (this.mediumView ? 280 : 340),
  maxWidth: this.workspaceView ? 460 : (this.mediumView ? 340 : 460)
})

百分比负责跟随窗口,minWidth/maxWidth 防止卡片过窄或搜索区无限变宽。响应式布局通常需要比例与约束共同工作。

十六、内容最大宽度如何保护阅读体验

tabContent() 在大屏上设置 1120vp 或 1480vp 上限。页面内部的表单和详情还会设置 720vp 上限。

这解决两类问题:

  • 超宽屏长文本一行过长,阅读效率下降;
  • 页面为了填满空间而把按钮和输入框拉得过大。

可用空间不等于必须占满的内容宽度。

十七、嵌入模式如何复用详情与匹配页

ItemDetailPageMatchResultsPage 都支持 embedded

  • 独立页面模式显示自己的 Header 和返回逻辑;
  • 嵌入模式隐藏重复导航,填充中栏或右栏;
  • 业务加载、状态文案和按钮仍复用同一实现;
  • 候选点击通过回调更新中栏,而不是再次 push 路由。

复用的是页面主体,不是把独立页面截图放进大屏容器。

十八、断点切换不应重新初始化业务层

aboutToAppear() 初始化 ReportServiceClaimServiceHandoffServiceModerationService 和设置服务。宽度变化只更新 screenWidth,不重新创建 Repository 或重置数据。

如果在每个 Shell 分支中初始化业务层,拖动窗口可能形成重复数据库打开、重复请求或页面状态闪烁。

十九、loading、empty、error 在四档中仍需存在

响应式布局不能只对"有四条数据"进行设计。HomePage 的左列同时处理:

  • loading:正在加载本机记录;
  • error:错误容器与重新加载;
  • empty:登记丢失/拾得入口;
  • 组合筛选无结果:重置筛选;
  • 正常列表:可选中记录卡片。

在 xl 中,左列没有选择时,中栏和右栏也有各自空态,不能显示空白框。

二十、键盘焦点如何跨 Shell 保持可达

三种导航都使用原生 Button,并提供 accessibilityTextaccessibilityDescription。2026-08-12 的模拟器验收记录显示:

  • xl 焦点从搜索、筛选、记录、中栏操作、候选、侧栏再回到搜索;
  • Phone 焦点从搜索、筛选、四张记录、四个底栏入口再回到搜索;
  • 精确 1280vp 时 Tab/Shift+Tab 能进入侧栏与右栏候选。

这证明了当时模拟器输入链路,不能替代实体键盘或屏幕阅读器验收。

二十一、运行证据必须带日期和环境

本系列引用的响应式证据来自 2026-08-12:

  • MateBook Pro API 22 模拟器;
  • Pura 90 API 24 模拟器;
  • xunji-responsive-focus-state-audit.md
  • xunji-exact-xl-breakpoint-input-audit.md
  • 当时本地脚本与 APP 构建通过。

当前文章核对了现有断点代码仍然存在,但没有把旧模拟器证据冒充成 2026-08-26 的实体 2-in-1 复测。

二十二、首发 manifest 为什么仍只声明 phone

当前 entry/src/main/module.json5 明确:

json 复制代码
"deviceTypes": ["phone"]

项目选择先以 Phone 降低首发审核成本,保留 Tablet/2-in-1 响应式代码和设计资产供后续版本使用。

因此准确表述是"代码包含四档响应式 Shell,并在模拟器完成过断点验收";不能表述为"应用市场当前支持四类设备"。

二十三、模拟器与实体设备的验证边界

模拟器可以验证布局切换、焦点事件和页面状态;实体设备仍要单独检查:

  • 系统缩放和窗口吸附;
  • 触控板、鼠标滚轮与蓝牙键盘;
  • 折叠屏铰链与安全区;
  • 系统字体和输入法;
  • 性能、重绘和多窗口生命周期;
  • 商店设备声明与实际安装兼容。

任何一项未运行都应写成 not run

二十四、推荐的响应式 Shell 测试矩阵

宽度 预期 Shell 重点断言
377vp Bottom + 单栏 底栏、详情 push、单手操作
599vp Bottom + 单栏 临界点前不提前分栏
600vp Top + 双栏 导航切换、左列最小宽
839vp Top + 双栏 临界点前仍为 md
840vp Side + 双栏 侧栏出现、Tab 状态保持
1278.95vp Side + 双栏 右栏不出现、详情保持
1280vp Side + 三栏 34/44/22、候选联动

测试数据、筛选条件和选中候选应固定,否则视觉差异无法定位到布局还是数据。

二十五、工程复盘:响应式首先是状态架构

四档 Shell 能稳定切换的关键,不是写了四个 if,而是把 selectedTabpathStack、查询条件、选中记录和业务数据放在布局分支之上。Shell 只决定导航和编排,Service 与 Repository 仍是同一权威来源。

如果布局分支拥有自己的状态副本,窗口往返必然出现筛选重置、路由丢失和候选切换。

二十六、本文小结

"寻迹校园"用 600、840、1280 三个边界建立 sm/md/lg/xl 四档 ArkUI Shell:Phone 底栏、md 顶栏、lg 侧栏双栏、xl 侧栏三栏。Index.ets 集中读取根宽度并复用同一导航栈,HomePage 只接收语义布局参数。

现有代码与 2026-08-12 模拟器证据能证明响应式架构和断点行为;当前首发 manifest 仍只声明 Phone,实体 Tablet/2-in-1、外设和商店支持不能由保留代码推导。

系列导航:第 37 篇 / 共 50 篇。上一篇:《用语义 Token 管理 Light/Dark》;下一篇:《34%/44%/22% 三栏工作台》。

相关推荐
RisunJan3 小时前
HarmonyOS 架构精读:从系统分层到应用模型
华为·架构·harmonyos
见山是山-见水是水3 小时前
从需求到页面:鸿蒙原生 Ability 与 ArkTS 交互的 ArkTS 原生实现
华为·harmonyos
见山是山-见水是水3 小时前
网络请求性能优化落地指南:让原生界面在模拟器里稳定运行
网络协议·华为·性能优化·harmonyos
2501_919749033 小时前
华为鸿蒙免费语音记录APP—小羊回响
华为·harmonyos·鸿蒙
less_121383 小时前
HarmonyOS WPS Open SDK:旧 HAR 迁移到统一接口联调指南
华为·sdk·harmonyos·wps·鸿蒙开发·文档编辑
yuhulkjv3353 小时前
告别复制粘贴式降级:纳米AI鸿蒙版导出word格式为何绕不开“AI 导出鸭”
人工智能·ai·word·harmonyos·ai导出鸭
HwJack204 小时前
鸿蒙 Accessibility Kit 全景解析:无障碍开发从哪里下手
microsoft·华为·harmonyos
大锅盖15 小时前
HarmonyOS 6.1.1 AI字幕新特性深度解析:从AICaptionComponent四大新增字段到K歌社交舞台黑霓虹紫深色架构全链路实战
人工智能·架构·harmonyos
m0_749690235 小时前
【寻迹校园 HarmonyOS NEXT 实战 38】34%/44%/22% 三栏工作台:超宽屏失物匹配的主从详情设计
华为·harmonyos·arkts·状态管理·大屏适配·主从详情