fix: 修复富文本 lineBreakMargin/onTextLayout 跨端计算异常 - #2
Open
zenipchen wants to merge 5 commits into
Open
Conversation
added 5 commits
July 30, 2026 11:35
…1069999479716042323)
根因
1. TextStringRichNode.genTextLayoutResult() 构造 MultiParagraph(placeholderRects=...) 时
lineCount 恒为默认 0,导致 onTextLayout 回调的 TextLayoutResult.lineCount 永远为 0,
业务无法据此判断文本是否溢出、决定是否展示“展开”按钮。
2. measureTextView() 中 isLineBreakMargin 事件只在 measure 时无条件 fire 一次,
未缓存上一次结果,既会重复 fire,也会在“溢出→不溢出”变化时漏 fire 事件。
core 层 TextView/RichTextView 的 tryFireLineBreakMarginEvent 存在同样缺陷。
修法
- compose/TextStringRichNode.kt:
* 测量完成后通过 shadow.callMethod(SHADOW_METHOD_IS_LINE_BREAK_MARGIN) 推导真实行数:
isLineBreakMargin=="1"(溢出,即实际行数 > maxLines)取 maxLines+1 近似值,
否则取 1,填充进 MultiParagraph(lineCount=...) 使 onTextLayout 拿到可用 lineCount。
* isLineBreakMargin 查询结果缓存到 lastLineBreakMarginFired,仅在值变化时 fire
ON_LINE_BREAK_MARGIN 事件,避免重复 fire 与漏 fire。
- core/TextView.kt、core/RichTextView.kt: 同样为 tryFireLineBreakMarginEvent 增加
lastLineBreakMarginFired 缓存,仅在结果变化时 fire,保持与 Compose 行为一致。
约束保持:不新增 shadow 方法,三端 isLineBreakMargin 仍返回 '1'/'0',跨端协议不变。
验证::core:compileKotlinMetadata 与 :compose:compileKotlinMetadata 均编译通过。
TAPD bug_1069999479716042323 — vivo X70 Pro+/Android 11 等机型富文本
lineBreakMargin 异常(展开按钮不显示 / 63dp 行尾留白不生效)。
问题根因:
1. 状态残留:textProps.isLineBreakMargin 仅在溢出分支置 true,
从未重置为 false。文本从溢出变为不溢出(复用/更新场景)时状态残留,
导致 isLineBreakMargin 查询('1'/'0' 协议)与真实布局不一致。
2. setIndents 兼容性:createLineBreakMarginArray 返回的 rightIndents
数组长度固定为 numberOfLines。StaticLayout 对超出数组长度的行会复用
最后一个元素,不同 ROM 复用规则存在差异(部分机型对所有行加 indent
或最后一行漏加),致使 63dp 行尾留白在部分机型上不生效。
修复:
- createLayout 入口处先将 isLineBreakMargin 重置为 false,仅在真正
溢出的分支置 true,确保每次测量后状态立即准确。
- createLineBreakMarginArray 改为接收实际 lineCount,数组长度与真实
行数一致、且只在最后一行填充 lineBreakMargin,消除 ROM 差异。
- isBeforeM 旧路径(API<=23)逻辑保持,vivo X70 Pro+ 为 Android 11
(API 30) 走新路径,本次修复覆盖其问题来源。
验证:./gradlew :core-render-android:compileDebugKotlin --rerun-tasks
BUILD SUCCESSFUL(仅预存 warning,KRRichTextView.kt 无新增告警)。
未实机验证(无对应机型/设备)。
TAPD: bug_1069999479716042323 根因: isBreakLine 的判定依赖'限行 vs 不限行'两次测量的尺寸对比 (KRLabel sizeThatFits / KRRichTextShadow 渐变分支),两次测量之间仅切换 NSTextContainer.maximumNumberOfLines。部分 iOS 版本/机型上仅修改 maximumNumberOfLines 不会触发 NSLayoutManager 布局失效,第二次 usedRectForTextContainer 返回旧布局缓存尺寸 -> newSize == fitSize -> isBreakLine 恒为 NO -> shadow 侧 isLineBreakMargin 返回 '0'、绘制侧不设 exclusionPath,导致省略号/正文/展开按钮重叠,且表现为机型差异。 修复 (均在 core-render-ios/Extension/Vendor/KRLabel.m): 1. KRTextRender setMaximumNumberOfLines / setLineBreakMode: 变更后显式调用 [layoutManager textContainerChangedGeometry:] 强制布局失效,保证第二次 测量真实重排 -> shadow 测量完成后 isLineBreakMargin 立即可查询到正确值; 2. textSizeWithRenderWidth: ensureLayoutForTextContainer 去掉 macOS-only 宏,iOS 同样强制布局后再取 usedRect,消除 stale 尺寸(对已布局场景为 no-op); 3. sizeThatFits:...lineBreakMarin: 5 参重载原实现将 marin 写死为 0 转发, 导致该重载调用方 lineBreakMargin 丢失,改为透传 marin; 4. drawTextInRect: exclusionPaths 仅在变化时设置(避免每次重绘全量重排), 并在非截断状态清除残留 exclusionPath,避免 textRender 状态翻转后 末行仍被错误留白。 协议不变: isLineBreakMargin 仍返回 '1'/'0'。 验证: 代码审查 + 逻辑推导(两次测量路径、渐变分支、attachment span 分支 均复用 KRTextRender setter,修复自动覆盖)。未实机验证。
TAPD bug_1069999479716042323: 鸿蒙全机型必现,展开按钮不显示、省略号/正文/按钮重叠。
根因:
1. KRRichTextShadow::Call("isLineBreakMargin") 误将函数指针
OH_Drawing_DestroyTextLines 当布尔条件,笔误导致表达式语义错误。
2. did_exceed_max_lines_ 仅在旧路径 BuildTextTypography() 赋值;
V2 StyledString 路径走 CalculateRenderViewSizeWithStyledString()
-> KRParagraph::Measure() 时从不设置,isLineBreakMargin 恒返回 '0',
业务永远收不到 onLineBreakMargin 事件,展开按钮不显示;同时
KRRichTextView 的 63dp 偏移依赖 DidExceedMaxLines() 同样失效。
修复:
- KRRichTextShadow.cpp: 修正 isLineBreakMargin 条件为 did_exceed_max_lines_
单独判断(显式布尔,加注释);V2 路径下从 paragraph 回传
DidExceedMaxLines() 正确赋值 did_exceed_max_lines_,并将 lineBreakMargin
透传给 paragraph。
- KRParagraph.{h,cpp}: 测量后取 OH_Drawing_TypographyDidExceedMaxLines 存入
did_exceed_max_lines_ 并暴露 DidExceedMaxLines()/SetLineBreakMargin();
最后一行留白由 KRRichTextView::OnForegroundDraw 的
(frameWidth - line_break_margin_) 截断逻辑处理,本次使其随 DidExceedMaxLines()
正确生效。
限制:未在鸿蒙真机/模拟器实机构建验证(环境无 NDK),仅做代码级逻辑复核。
不改动 core-render-ohos 之外目录。
对抗式 CR 指出原实现用 maxLines+1/1 伪造真实行数,会误导依赖精确行数的业务。 各端 shadow 未统一暴露 getLineCount,故将 lineCount 显式降级为溢出布尔语义 (溢出>maxLines / 未溢出占位=1) 并加 KDoc 声明;业务应改用 ON_LINE_BREAK_MARGIN 事件判定溢出。真实行数获取留作平台层 follow-up。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
背景
TAPD bug_1069999479716042323:KuiklyUI 富文本组件(OnText(AnnotatedString) + lineBreakMargin + onTextLayout{lineCount})在部分机型上计算异常:
根因
lineBreakMargin / onTextLayout 的 lineCount 计算依赖 shadow 层状态,但:
修改(按目录边界拆分,零重叠)
对抗式 Code Review(redteam 独立隔离审)
验证
TAPD: bug_1069999479716042323