本章导读:这是“寻迹校园 HarmonyOS NEXT 实战”系列第 39 篇。本文结合 Index.etsHomePage.etsxunji-responsive-focus-state-audit.mdxunji-exact-xl-breakpoint-input-audit.md,复盘窗口在 xl → lg → xl 之间往返时,如何保持搜索、筛选、左栏滚动、查询记录、候选预览与中间详情,并说明 1278.95vp/1280vp 精确断点证据为什么仍不能替代实体 2-in-1 验收。

1280vp 断点状态恢复原创封面图

上图为原创生成的断点测试插画,不是项目截图。窗口变化只切换可见布局,状态账本位于布局分支之上,因此右栏暂时隐藏不等于候选状态被删除。

一、断点 Bug 往往不是布局错,而是状态重置

页面从三栏缩到双栏再放大,最容易出现的不是宽度计算错误,而是:

  • 搜索关键词被清空;
  • 组合筛选面板收起;
  • 左栏滚动回到顶部;
  • 查询记录切回第一条;
  • 中栏从候选详情跳回查询本身;
  • 右栏恢复后忘记哪个候选已选中;
  • 路由栈被新 Shell 重建;
  • 重复触发 Service 初始化。

这些问题的共同根因,是把业务/交互状态放在只对某个断点存在的组件内部。

二、本章验证的精确临界点

测试设备是 2026-08-12 的 MateBook Pro API 22 模拟器,VPR 为 1.9:

根窗口像素宽度 逻辑宽度 预期布局
2430px 1278.95vp lg 双栏
2432px 1280vp xl 三栏

由于物理像素是离散值,2430px 无法精确得到 1279vp,但 1278.95vp 足以证明 <1280 分支;下一档 2432px 恰好命中 1280vp。

三、测试场景不是空页面缩放

为了验证状态恢复,测试先构造有意义的上下文:

  1. 在 xl 打开筛选面板;
  2. 滚动左侧记录列表;
  3. 选择一条查询记录;
  4. 在右栏点击候选 2;
  5. 确认中栏显示候选详情;
  6. 缩到 lg;
  7. 确认右栏隐藏但中栏仍是候选;
  8. 再放大到 xl;
  9. 确认筛选、滚动、查询和候选选择恢复。

只验证“三栏出现/消失”无法发现状态问题。

四、根宽度状态位于 Shell 之上

Index 保存:

@Local selectedTab: BottomTab = BottomTab.HOME;
@Local dataRevision: number = 0;
@Local screenWidth: number = 0;

screenWidth 只决定渲染哪种 Shell,selectedTabpathStack 不属于某个 Shell 分支。窗口变化不会重新创建 Index,因此全局导航上下文得以保留。

五、HomePage 状态也位于 compact/split 分支之上

HomePage 在 builder 之外保存:

@Local keyword: string = '';
@Local selectedFilter: HomeReportFilter = HomeReportFilter.ALL;
@Local selectedCategory: string = '';
@Local selectedAreaKeyword: string = '';
@Local selectedTimeRange: HomeTimeRange = HomeTimeRange.ALL;
@Local selectedColor: string = '';
@Local filterPanelVisible: boolean = false;
@Local selectedReportId: string = '';
@Local previewReportId: string = '';

compactHome()splitScreenHome()workspaceView 只是消费这些状态,没有各自创建副本。

六、为什么状态必须高于 if 分支

如果把 previewReportId 声明在右栏组件内部,lg 布局不渲染右栏时组件会销毁,恢复 xl 后只能重新选择默认候选。

当前设计把预览 ID 放在 HomePage,右栏只是根据它显示选中边框。即使右栏暂时不存在,中栏仍可以继续展示候选详情。

七、xl → lg 时到底隐藏了什么

当宽度从 1280vp 降到 1278.95vp:

  • workspaceView 从 true 变为 false;
  • 右栏 MatchResultsPage 不再渲染;
  • 左栏从 34% 变为 38%;
  • 中栏从固定 44% 变为 layoutWeight(1)
  • selectedReportId 不变;
  • previewReportId 不变;
  • 中栏仍用 previewReportId 加载详情。

隐藏的是辅助视图,不是业务选择。

八、lg → xl 时如何恢复候选标记

恢复到 1280vp 后,右栏重新创建,并接收:

selectedCandidateId:
  this.previewReportId === this.selectedReportId ? '' : this.previewReportId

如果中栏仍在预览候选,右栏对应卡片立即显示“正在中间预览”和品牌边框。恢复不依赖重新点击。

xl、lg、xl 状态账本与可见区域原创图

上图把持久状态、短生命周期交互状态和布局可见性分开。lg 阶段只暂停右栏呈现,查询与预览状态仍留在 HomePage。

九、selectedReportId 与 previewReportId 的断点意义

两者分别表示:

  • selectedReportId:左栏查询来源;
  • previewReportId:中栏当前详情。

断点往返时,查询来源必须保持,候选预览也应保持。一个 ID 无法同时表达这两个事实,会迫使页面在缩放时选择丢弃其中之一。

十、筛选面板展开态为什么值得保留

用户在 xl 展开组合筛选后缩小窗口,通常只是调整窗口,不代表取消任务。如果 filterPanelVisible 随布局重建变回 false,用户会误以为筛选条件被清除。

当前展开态保存在 HomePage,因此往返后仍保持。显示形式可以改变,但用户意图不应被断点覆盖。

十一、筛选值如何与列表刷新协调

搜索或筛选变化会调用 refreshReports()。刷新后页面先判断原查询是否仍在结果中:

const selectionAvailable = this.reports.some(
  (report: ItemReport) => report.id === this.selectedReportId
);

只有查询被筛掉时才选择新的第一条记录。窗口变化本身不调用重置筛选,因此不会改变结果集合。

十二、左栏滚动位置属于视图状态

左栏 Scroll 在布局往返时保持位置,是 2026-08-12 验收的一部分。滚动位置不是 Repository 数据,也不需要写入数据库;它属于当前页面会话的短生命周期状态。

但如果整个 HomePage 被断点分支替换为不同页面实例,Scroll 也会重建。当前实现保持同一 HomePage,只改变内部布局。

十三、右栏滚动恢复为什么曾是 not applicable

第一轮验收中,最大化窗口内容高度约 942vp,当前业务最多返回 3 条候选,全部已经可见,没有有效额外滚动距离,因此“右栏滚动恢复”被标记为 not applicable

后续精确 1280vp 验收在更窄右栏中注入滚动,候选 2、候选 3 进入视口,证明滚动容器可达。但这仍不是“滚动偏移在所有窗口往返中持久化”的完整证明。

十四、Navigation 为什么不能随 Shell 重建新实例

Phone、md、lg/xl 的外壳不同,但 pathStackIndex 的单一字段。假如每个 builder 内部临时创建 new NavPathStack(),断点切换会丢失当前详情、发布或认领路径。

共享路由栈让 Shell 切换只影响导航呈现,不影响用户已经进入的流程。

十五、业务初始化为什么不能绑定 onAreaChange

onAreaChange 可能在窗口拖动过程中高频触发。当前回调只在宽度变化时更新数字,不打开数据库、不请求匹配、不重建 Service。

业务初始化发生在 aboutToAppear(),并通过现有 Service 统一执行。把 Repository 初始化放进宽度回调会形成请求风暴和状态闪烁。

十六、边界判断要避免重叠与空洞

当前范围为:

sm: width < 600
md: 600 <= width < 840
lg: 840 <= width < 1280
xl: width >= 1280

每个宽度只属于一个档位,没有 600vp 同时命中两种布局,也没有 839.5vp 落入空白。测试必须覆盖等号所在的点,而不只测 500、900、1500 三个普通宽度。

十七、Number(newArea.width) 的输入风险

Area.width 被转换为 number 后参与比较。实现应关注:

  • 初次布局前 screenWidth 为 0;
  • 宽度字符串能否稳定转换;
  • 系统缩放产生小数 vp;
  • 浮点值在 1280 附近的离散性;
  • 同一个物理像素变化是否频繁跨界。

本次 1278.95/1280 证据正是对小数逻辑宽度的实际检查。

十八、为什么需要往返而不是单向切换

单向 lg → xl 只能证明第三栏出现。xl → lg → xl 才能检查:

  • 隐藏组件是否销毁权威状态;
  • 中栏是否保留候选;
  • 右栏能否恢复选中态;
  • 筛选与滚动是否仍在;
  • Tab 焦点链是否重新闭合;
  • 列表是否被重复刷新。

状态恢复测试必须包含“回到原布局”。

十九、焦点状态与数据状态不能混为一谈

断点切换后,具体 DOM/ArkUI 节点可能被替换,键盘焦点不一定能保持在完全相同的按钮上;但查询、筛选和候选选择仍必须保持。

测试应分别记录:

  • 数据状态:Report、候选、筛选值;
  • 交互状态:选中、展开、滚动;
  • 焦点状态:当前可聚焦节点与顺序;
  • 路由状态:页面栈;
  • 布局状态:sm/md/lg/xl。

一项通过不能覆盖其他项。

二十、2026-08-12 的验收结果

xunji-responsive-focus-state-audit.md 记录:

  • xl → lg → xl 状态恢复 passed;
  • 筛选面板、左栏滚动、查询记录、候选 2 预览和中栏详情保持;
  • xl 焦点链在应用内闭环;
  • 377vp Phone 回归 passed;
  • 当时本地测试 15/15、APP 构建 passed。

xunji-exact-xl-breakpoint-input-audit.md 进一步记录:

  • 1278.95vp 为 lg;
  • 1280vp 为 xl;
  • 候选点击联动 passed;
  • Tab/Shift+Tab 输入 passed;
  • 右栏滚动 passed。

二十一、为什么不能把旧构建结果当成今天的新构建

上述记录来自明确日期和当时基线。当前文章重新读取了现有 Index.etsHomePage.ets,确认状态结构仍存在,但没有重新执行 2026-08-26 的 MateBook 模拟器安装与构建。

因此文章引用“2026-08-12 验收 passed”,不会写成“今天重新真机通过”。

二十二、实体 2-in-1 仍需补哪些证据

模拟器输入通道无法覆盖:

  • 实体窗口吸附与系统缩放;
  • 触控板滚动惯性;
  • 鼠标焦点与 hover;
  • 蓝牙键盘组合键;
  • 外接显示器 DPI 切换;
  • 睡眠恢复和多窗口生命周期;
  • 折叠形态与铰链区域。

这些项目应继续标记为 not run,直到出现相应设备记录。

二十三、首发仅 Phone 对测试结论的影响

当前 manifest 只声明 Phone。大屏断点测试属于保留能力的工程验证,不等于当前 AGC 包面向 2-in-1 分发。

后续恢复设备类型前,应基于最终 release 包重新执行矩阵,而不是直接复用开发阶段的模拟器结论。

二十四、可自动化的状态断言

断点测试可以把状态写成结构化断言:

before:
  width=1280, query=A, preview=C2, filterOpen=true
shrink:
  width=1278.95, rightPanel=false, query=A, preview=C2
restore:
  width=1280, rightPanel=true, query=A, preview=C2, candidate2Selected=true

截图用于视觉证据,布局树/状态记录用于精确断言。只比较截图容易受字体、时间和动画影响。

二十五、工程复盘:布局可变,用户意图必须稳定

窗口变化是展示环境变化,不是用户重新开始任务。查询、筛选、候选预览和路由表达用户意图,生命周期应该高于 Shell 和右栏组件。

将状态提升到稳定父组件后,断点分支可以自由出现或消失,而无需把所有状态写入全局存储或数据库。

二十六、本文小结

“寻迹校园”的 1280vp 状态恢复依赖两层稳定状态:Index 保存全局导航与宽度,HomePage 保存筛选、查询与预览;lg/xl 分支只改变右栏可见性和列宽。selectedReportIdpreviewReportId 的分离,使候选详情在右栏隐藏期间仍能保留。

2026-08-12 的 1278.95vp/1280vp 模拟器证据证明了当时的临界切换、往返状态和输入链路;实体 2-in-1、外设、当前 release 包与商店设备支持仍需独立验证。

系列导航:第 39 篇 / 共 50 篇。上一篇:《34%/44%/22% 三栏工作台》;下一篇:《键盘焦点与可访问名称》。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐