MD 阅读 Android 版 · 系统栏切换与鸿蒙侧同步回归
Android 版补齐菜单第 3 项「隐藏系统栏 / 显示系统栏」(只切系统栏可见性、不切布局模式),真机量化验收位移精确等于状态栏高度;鸿蒙侧同步后完成编译与解包取证,并新增本地无头工具验证图形高度算法(9/9 贴合)。
日期:2026-09-24 版本:1.1.4(versionCode 1001004) 真机:Mate 30 Pro(LIO-AN00 / Android 12 / 1176×2400)
一、本轮两项交付
| # | 事项 | 状态 |
|---|---|---|
| 1 | 菜单补「隐藏系统栏 / 显示系统栏」(对齐鸿蒙菜单第 3 项) | ✅ 实现并真机验收 |
| 2 | 鸿蒙侧 graph.html 同步后的图形高度与全屏回归 | ⚠️ 编译 + 解包 + 算法层已验证;真机回归未做(设备不在线) |
二、菜单「隐藏系统栏 / 显示系统栏」
2.1 实现口径
对齐鸿蒙 toggleFullScreen() → win.setWindowSystemBarEnable([]):
val controller = WindowCompat.getInsetsController(win, view)
controller.systemBarsBehavior = BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
controller.hide(WindowInsetsCompat.Type.systemBars()) // 或 .show(...)
只切系统栏可见性,不切布局模式 —— 沉浸式布局始终不变。这一条很关键: 鸿蒙上早先的「双倍留白」正是来自「切布局模式 + 切系统栏」两个动作叠加,本次刻意避开。
菜单位置与鸿蒙逐项对齐(第 3 项):
源码/预览 → 【隐藏系统栏 / 显示系统栏】 → 打开文件 → 分享 Markdown → 语音朗读 → ─── → 设置 → 关于
2.2 真机验收(量化)
标题栏文本 demo.md 的 top 坐标(UI dump 实测):
| 状态 | 标题栏 top | 菜单第 3 项文案 |
|---|---|---|
| 初始(系统栏可见) | 147 | 隐藏系统栏 |
| 点「隐藏系统栏」后 | 48 | 显示系统栏 |
| 点「显示系统栏」后 | 147 | 隐藏系统栏 |
- 位移 99px = 状态栏 33dp × 3.0(density) —— 精确等于状态栏高度,无多补、无少补、无双倍留白。
- 反向切换精确回到 147,往返三次一致(147 → 48 → 147 → 48)。
- 视觉:状态栏与底部导航条同时消失,标题栏顶到屏幕最上沿,内容铺满全屏。
2.3 边界用例
| 用例 | 结果 |
|---|---|
| 隐藏态下切后台再回前台 | 系统栏保持隐藏(EMUI 未擅自恢复),菜单文案与真实状态一致 ✅ |
| 隐藏态下打开图形全屏层 | 全屏层蓝栏同步上移(top 147 → 48),图形仍居中 ✅ |
| 隐藏态下打开朗读面板 | 面板正确贴底,无多余留白 ✅ |
| 全屏层 + 返回键 | 被全屏层正确接住,未穿透到 Activity ✅ |
| 崩溃检查 | logcat crash buffer 为空 ✅ |
2.4 一处平台差异(已查清,非缺陷)
隐藏系统栏前后,朗读面板的 bounds 完全没变。用 dumpsys 查清原因:
显示态:ITYPE_STATUS_BAR frame=[0,0][1176,99] visible=true
ITYPE_NAVIGATION_BAR frame=[0,0][0,0] visible=false
隐藏态:ITYPE_STATUS_BAR frame=[0,0][1176,99] visible=false
ITYPE_NAVIGATION_BAR frame=[0,0][0,0] visible=false
本机(EMUI 12 / 手势导航)导航条 insets 恒为 0,所以面板底部避让本来就与系统栏显隐无关, 面板位置不变是正确行为。
这与鸿蒙「隐藏系统栏后避让区归零」最终效果一致(都是 0),只是到达路径不同: 鸿蒙是「有 → 归零」,Android 本机是「一直是 0」。三键导航 ROM 上是否归零需另测。
三、鸿蒙侧 graph.html 同步后的回归
⚠️ 前提说明:本轮鸿蒙测试机未接入(hdc list targets 为空;USB 上只有 Mate 30 Pro-Android 机)。
真机回归无法执行。以下是设备缺失下能做到的全部验证,不要当作真机通过。
3.1 编译验证 ✅
hvigorw assembleHap -p product=device -p buildMode=debug --no-daemon
→ BUILD SUCCESSFUL in 2 min 48 s
产物:entry/build/device/outputs/default/entry-default-signed.hap
3.2 解包取证 ✅(确认打进包里的就是新版)
从 HAP 里解出 resources/rawfile/graph.html(33,344 字节)与 Android 版逐行 diff:
- 与 Android 版仅剩
htmlLabels一处差异(鸿蒙true保留 HTML 富文本标签 / Androidfalse规避foreignObject不光栅化),加上各自的说明注释 —— 完全符合预期。 - 新版特征命中:
fitViewBoxToContent/contentBox共 6 处。 - 高度上报通道完好:
ArkTSBridge.updateHeight(h)仍在。 - 鸿蒙调用侧接口(
renderGraph(code, true)/renderSvg(code, true)/ 轮询getInlineHeight())与页面导出签名完全对齐。
3.3 高度算法本地验证 ✅(新增工具,见第四节)
用本地 Chrome 渲染同一份鸿蒙版 graph.html,逐个图形比对「上报高」与「图形实际需要的高」:
| 视口 | 结果 | 最大差值 | 平均差值 |
|---|---|---|---|
| 400px(模拟真机 CSS 宽) | 9/9 PASS | 0.88px | 0.44px |
| 320px(强制宽图缩放,覆盖另一分支) | 9/9 PASS | 1.00px | 0.56px |
| (对照)Android 版 @ 392px | 9/9 PASS | 0.84px | 0.48px |
老板报过的两个问题点,现在是这个数:
- 7.5 状态图:图形实际高 348.0,上报 364.0 → 差值 +0.02px,完整不裁 ✅
- 7.8 时间线:图形实际高 204.1,上报 221.0 → 差值 +0.88px,无大块留白 ✅
3.4 仍然必须真机做的部分 ❌
- mermaid 在 ArkWeb 里的绝对渲染尺寸与字体度量(桌面 Chrome ≠ ArkWeb)
- 手势:双指缩放、双击放大、拖动平移
- 全屏层的几何与返回键行为
- 真机上的内联高度观感(数值贴合 ≠ 看起来舒服)
→ 需要老板把鸿蒙测试机接上 USB,我再跑一轮(约 10 分钟,走 harmonyos-device-verify 流程)。
四、新增工具 _tools/graph-height/
C:/bzm/harmony/_tools/graph-height/verify.mjs(零 npm 依赖,Node 22 内置 WebSocket 驱动 CDP)
它验的:getInlineHeight() 的上报值是否等于图形真正需要的高度。
为什么本地能验:该公式与浏览器 max-width:100% + height:auto 是数学等价的,只依赖 viewBox 与容器宽,与平台无关。
分工:本地验「算法自洽」,真机验「ArkWeb 实际渲染 + 交互」。本工具 FAIL 则真机必有问题,可省一轮装机。
灵敏度自检(证明它不是「永远 PASS」的工具):往 #container svg 注入 min-height:260px 制造「高度不够」,
结果 9 个图形 FAIL 6 个,最大差值 −187px、符号为负(= 裁切),方向正确。
已知局限:桌面 Chrome 字体度量 ≠ ArkWeb,故「等价关系」可移植,但绝对像素高两端会有差异。
五、待办 / 待批示
| # | 事项 | 说明 |
|---|---|---|
| A1 | 鸿蒙真机回归(本轮未做) | 需接入鸿蒙测试机;跑图形高度观感 + 全屏手势 |
| A2 | Android 工程纳入 git | 上轮已提,仍未批示;C:\bzm\android\mdreader-lite 至今无版本控制 |
| A3 | 鸿蒙工程积压提交 | git 根在 c:/bzm/harmony/,当前 13 个已跟踪文件改动未提交(含本轮同步的 graph.html / MermaidView.ets / SvgView.ets),最近提交还停在 1.0.4 时代。建议尽快提交一次,否则回滚无锚点 |