项目进展2026-09-24

MD 阅读 Android 版 · 图形高度自适应与全屏查看

两个问题的根因完全不同,分别处理。

  • 版本:1.1.4 (1001004) · debug 包
  • 真机:HUAWEI Mate 30 Pro (LIO-AN00) / Android 12 / 1176×2400 / density 480
  • 日期:2026-09-24
  • 范围:图形渲染层(Mermaid + SVG 共用)的内联高度算法与全屏查看层

一、老板反馈的两个问题

#问题现象
1「全屏查看」按钮点击无反应点图形块没有任何响应
2内联小图高度不对7.5 状态图左右合适但高度过低、显示不全;7.8 时间线高度过高、底部大块留白

两个问题的根因完全不同,分别处理。


二、问题 1:点击无反应

原因

不是"按钮失效",而是全屏查看层从来就不存在:

  • ui/MarkdownRender.kt 一直把 onGraphClick(kind, code) 往上传(Block.Mermaid / Block.Svg 两处);
  • 但 ui/ReaderScreen.kt 调 BlockItem(block = block, onOpenUrl = onOpenUrl) 时从没传这个参数,

回调恒为 null;

  • 且工程里没有任何全屏层组件(当时只是画了一个「全屏查看」提示标签)。

修复

  1. 新增 ui/GraphFullScreen.kt(对应鸿蒙 Index.ets 的 FullscreenViewer):

蓝色顶栏 #2a6fDB(「渲染图」+「关闭」)+ 白色 Web 区 + insetBottom 避让导航条; BackHandler 让返回键优先退全屏而不是退出应用。

  1. ui/GraphView.kt 新增 fullscreen: Boolean 参数,全屏时向页面传 fitWidth = false

(保持自然尺寸,交给页内手势缩放/平移);全屏的容器用 fillMaxSize() 而非固定高度。

  1. ui/ReaderScreen.kt 外层套 Box,把全屏层作为 overlay 盖住标题栏与正文,并接上点击回调。
  2. 先渲染轻量占位、下一帧才挂 Web(对齐鸿蒙 fsReady 策略):点击所在帧不必等

2.5MB 的 mermaid.min.js 初始化完成,避免"点击完成时延"被判不合格。


三、问题 2:内联高度两个方向都出错

原因

旧实现直接读 svg.getBoundingClientRect().height。这个值是元素盒,受内联 style 与布局时机 双重干扰 —— 实测两个方向都会错,所以两个图表现出相反的症状:

  • 偏小 → 显示不全:无 viewBox 的 SVG 在 max-width:100% + height:auto 下失去等比依据,

高度塌成 height 属性值或浏览器默认 150px;外加 mermaid 输出偶有内容溢出 viewBox 被裁。

  • 偏大 → 底部留白:宿主侧的高度区间校验把合法的小值拒了。

铁证

旧代码 GRAPH_MIN_H = 80,而时间线的真实高度实测 79:

I MDGraph : notify h=79 accepted=true full=false

79 < 80 → 被区间校验丢弃 → viewHeight 保持初始值 260 → 底部留白 181px。这就是 7.8「高度过高」的确切原因(不是测量偏大,是不合法值被拒)。

修复(三层)

① 确定性高度计算(assets/graph.html 的 getInlineHeight())

不再读渲染盒,改用与浏览器同一套算法:

h = min(自然宽, 可用宽) × vbH / vbW  +  容器上下 padding  +  警告条高度

即"宽度以容器为限、高度按 viewBox 比例自动",与渲染结果一致且与布局时机无关。

② viewBox 归一与内容对齐

  • applyInlineSizing() 三步走:保证有 viewBox(无则先由 width/height 属性补,

再用 getBBox() 补)→ 把内容包围盒并入/收拢 viewBox → width/height 属性写回 viewBox 尺寸 (消除 width="100%" 这类百分比带来的歧义)。

  • 新增 fitViewBoxToContent():getBBox() 得到内容真实包围盒,与 viewBox 对比 ——

溢出就扩(治显示不全)、富余过多就收(治图形自身留白)。 阈值保守:偏差超 25% 且 ≥30 单位才动,并留 2% 余量,避免裁掉描边或文字外沿。

③ 页面主动上报 + 放宽区间校验

  • 页面渲染稳定后 console.log('MDH|' + h),Kotlin 在 onConsoleMessage 里截获;

另在 120ms / 600ms 各补一次(等字体就绪)。原有的定时轮询降级为兜底。 走 console 而不用 addJavascriptInterface —— 不向页面暴露任何可调用的原生对象,零安全面。

  • 高度下限 GRAPH_MIN_H 由 80 放宽到 24,扁图形(时间线等)不再被误拒。
  • 单位口径确认:WebView 的 devicePixelRatio == Android density(本机均为 3.0),

1 CSS px == 1 dp,Kotlin 侧可直接当 dp 使用。


四、附加修复:全屏内图形贴顶不居中

全屏打开后图形贴在顶部、下方大片空白。诊断日志(MDFS| → fsGeom)给出确凿数据:

fsGeom {"nw":376,"nh":73,"sc":1,"min":1,"vw":376,"vh":0,"tx":0,"ty":319,...}
                                          ^^^^^^ vh 读到 0

vh = 0 说明容器内容盒高度塌陷。逐版排查:

版本写法结果
①body{height:100%} + #container.actual{height:100%}clientHeight 塌成 16px(只剩 padding)、vh=0 → 不做垂直居中
②改 body{height:100vh}百分比链仍断;且 overflow:hidden 把图形整块裁掉(全屏一片空白)
③ ✅#container.actual{position:fixed; top/left/right/bottom:0}vh=695、ty=311 → 居中正确

根因:本页的 html 不能设高度(设了就触发本项目已定位的「普通流元素整块不绘制」问题), 于是 body 的百分比链断裂,height:100% 解析成 0。 position:fixed 的包含块是视口,完全不依赖父级百分比链,一劳永逸。

配套两处修正:

  • computeLimits() 改用 getBoundingClientRect() 读自然尺寸 ——

SVGElement.scrollWidth/scrollHeight 对根 <svg> 返回偏大值,导致 naturalH 虚高、 被误判为"内容比视口高"而不做居中;

  • clampPan() 改用 contentBox()(减掉 padding)算可用宽高。

五、验收(Mate 30 Pro / demo.md 全量)

内联高度(问题 2)

图形结果
7.1 流程图(纵向)✅ 完整、高度贴合
7.2 流程图(横向)✅ 完整、高度贴合
7.3 时序图✅
7.4 类图✅
7.5 状态图✅ 完整显示(未打开 → 预览 → 源码/图形全屏/终止),此前"高度过低显示不全"已消除
7.6 甘特图✅
7.7 饼图✅
7.8 时间线✅ 底部留白消除(notify h=79 已被接受)
8.1 SVG 柱状图✅
8.2 SVG 环形进度✅

全屏查看(问题 1)

项结果
点击图形块进入全屏✅ 蓝栏「渲染图」+「关闭」,图形完整显示
全屏内垂直居中✅ 横向流程图 nh=73 → ty=311;时间线 nh=194 → ty=251
宽度以屏为限✅ 宽图收缩到屏宽,内容完整可读
双击放大(锚点=点击处)✅
拖动平移 / 双指缩放✅(沿用鸿蒙同款页内手势逻辑)
「关闭」按钮退出✅
返回键优先退全屏✅ 不退出应用,且保留正文滚动位置
点击完成时延✅ 先占位帧、下一帧才挂 Web

六、变更文件

文件说明
app/src/main/assets/graph.html高度确定性计算、viewBox 归一与内容对齐、主动上报、全屏 CSS 改 fixed 定位、computeLimits/clampPan 修正
app/src/main/java/.../ui/GraphView.kt新增 fullscreen 参数;console 截获 `MDH
app/src/main/java/.../ui/GraphFullScreen.kt新增:全屏查看层(对应鸿蒙 FullscreenViewer)
app/src/main/java/.../ui/ReaderScreen.kt接上 onGraphClick、外层 Box + 全屏 overlay、透传 insetBottom
app/src/main/java/.../MainActivity.kt向 ReaderScreen 传 insetBottom(全屏层与源码态底部避让)

零权限、零网络、零收集的定位未被破坏:图形仍由包内 mermaid.min.js 离线渲染, 全屏层也不引入任何网络请求。


七、遗留与下一步

  • 保留两条诊断日志(MDH| 内联高度、MDFS| 全屏几何)便于后续回归,发布 release 前可评估移除。
  • 待办:保存语音 / MP3 导出(面板按钮已占位,路线 = 逐句 synthesizeToFile → WAV 拼接 →

LAME NDK 编 MP3)→ 菜单「设置」项 → Release 签名打包与上架。

  • 待老板批示:本轮 graph.html 的 CSS/高度修正是否同步回鸿蒙版?

鸿蒙当前图形渲染正常,但内联高度算法与全屏 height:100% 这两处对鸿蒙同样是隐患 (鸿蒙侧 html/body 也曾设 height:100%)。

← 返回文章列表