HarmonyOS7 从零实现富文本收起展开路线图:照这个顺序写不容易乱
文章目录
前面 14 篇把项目拆开讲了。最后这一篇换个角度:如果你自己从零写一个 HarmonyOS7 富文本收起展开功能,应该按什么顺序来?
项目地址:https://gitcode.com/HarmonyOS_Samples/TextExpand
我不会让你一上来写 ParagraphBuilder。那样太容易乱。更稳的路线是从页面、数据、显示、测量、交互一步步推进。
第一步:先做一个能打开的页面
先别管富文本,保证页面能跳转、能显示列表。
首页按钮可以这样写:
Button('进入富文本示例')
.width('100%')
.onClick(() => {
this.getUIContext().getRouter().pushUrl({
url: 'pages/RichTextExpand'
});
})

目标很简单:点按钮能进入页面。这个没跑通,后面所有功能都没地方展示。
第二步:把信息流卡片搭出来
先写卡片结构:头像、昵称、正文区域、图片、操作栏。
可以参考项目里的 RichItemPart:
ListItem() {
Flex({ direction: FlexDirection.Column }) {
Row() {
Image(this.profileImg)
Column() {
Text($r('app.string.text_expand_text_name'))
Text($r('app.string.text_expand_text_date'))
}
}
RichTextExpandView({
dataModel: this.dataModel,
textSectionAttribute: new RichTextSectionAttribute(this.rawTitle),
lastSpanAttribute: new LastSpanAttribute(0, 2, this.handleContent(), 16)
})
}
}
先让页面长得像样,再逐步补逻辑。
第三步:设计富文本数据模型
不要直接传一整段 HTML 或字符串。先拆成片段:
export class RichTextContentModel {
index: number = 0;
length: number = 0;
type: string = '';
images: string[] = [];
content: string = '';
link: string = '';
fontColor: string = '#000';
fontSize: number = 16;
imgWidth: number = 16;
imgHeight: number = 16;
shortContent: string = '';
}
这一步很重要。后面截断、渲染、点击链接,全靠这个模型撑住。
第四步:先完整渲染,不急着折叠
先把 textArray 完整画出来:
Text() {
ForEach(this.textModifier.textContentArray, (item: RichTextContentModel) => {
if (item.type === 'text') {
Span(item.content)
.fontSize(item.fontSize)
.fontColor(item.fontColor)
} else if (item.type === 'images') {
ForEach(item.images, (url: string) => {
ImageSpan($r('app.media.' + url))
.width(item.imgWidth)
.height(item.imgHeight)
})
} else if (item.type === 'link') {
Span(item.content)
.fontColor(item.fontColor)
}
})
}
只要完整渲染没问题,再做折叠。别同时调渲染和截断,排查会很痛苦。
第五步:用 ParagraphBuilder 计算行数
能显示后,再判断是否超过最大行数:
let paragraph = TextUtils.getParagraph(
this.dataModel.textArray,
this.dataModel.fontSize,
this.textSectionAttribute.constraintWidth
);
this.textModifier.needProcess =
paragraph.getLineCount() > this.textSectionAttribute.maxLines;
短内容不显示“展开”,长内容才进入截断逻辑。
第六步:给 ... 展开 留位置
折叠不是简单截到第三行末尾。要先测按钮宽度:
const minLinesTextSize = uiContext?.getMeasureUtils().measureTextSize({
textContent: suffix + lastSpan,
fontSize: dataModel.fontSize,
});
const widthMore = uiContext?.px2vp(Number(minLinesTextSize?.width));
然后把第三行的 x 坐标往左挪,避免按钮换行。
第七步:用坐标反查截断字符
关键 API 是:
let positionWithAffinity = paragraph.getGlyphPositionAtCoordinate(x, y);
let index = positionWithAffinity.affinity === text.Affinity.UPSTREAM
? positionWithAffinity.position
: positionWithAffinity.position + 1;

拿到全局 index 后,再映射回 textArray 里的片段,生成 shortContent。
第八步:加点击切换状态
状态切换可以参考项目:
process(): void {
if (this.expanded) {
this.expandText();
this.expanded = false;
this.textModifier.exceedOneLine = true;
} else {
this.expanded = true;
this.textModifier.exceedOneLine = false;
this.collapseText();
}
}
别忘了短文本不响应点击:
if (!this.textModifier.needProcess) {
return;
}
第九步:补齐链接和图片边界
示例项目已经能跑通常规场景。如果你做业务项目,还要继续增强:
- 链接点击跳转真实页面。
- 图片片段被截断时单独处理。
- 不同字号片段参与排版。
- 横竖屏或不同设备宽度重新计算。
- 列表复用时保存每条内容展开状态。
这些不是第一版必须做,但上线前最好考虑。
最终效果再对一下
富文本折叠:

富文本展开:

如果你的效果和它接近,说明主流程基本没问题。
写在最后
这套 HarmonyOS7 富文本收起展开案例,真正值得学的不是某一个 API,而是完整拆解问题的方式:先建模,再排版,再截断,再交互。
别一上来就追求完美。先让完整内容显示,再让折叠跑通,最后处理边界。按这个顺序写,真的不容易乱。
更多推荐



所有评论(0)