HarmonyOS 鸿蒙Next中文本展开折叠:从纯文本到富文本的完整解决方案
HarmonyOS 鸿蒙Next中文本展开折叠:从纯文本到富文本的完整解决方案 在内容类应用中,处理长文本的展示与交互是高频需求。如何在有限空间内优雅地呈现内容,并提供流畅的展开/收起体验?本文将从笔者自身经验触发,从纯文本到富文本,和大家聊聊从我在HarmonyOS中的实现方案。

一、 纯文本展开折叠:精准计算的简洁之道
场景需求
- 纯文本内容超过2行时显示"…展开"
- 点击后展示全文,并显示"收起"按钮
- 需要精确计算截断位置

实现原理

通过文本测量API计算出恰好显示指定行数的文本内容,使用二分法高效定位截断点。
核心代码实现
// 判断是否需要展开折叠功能
getIsExpanded() {
// 测量完整文本高度
let titleSize: SizeOptions = this.uiContext.getMeasureUtils().measureTextSize({
textContent: this.textSectionAttribute.title,
lineHeight: this.textSectionAttribute.lineHeight,
constraintWidth: this.textSectionAttribute.constraintWidth,
fontSize: this.textSectionAttribute.fontSize
});
let height = this.getUIContext().px2vp(Number(titleSize.height));
// 判断是否超过2行
if (height <= this.textSectionAttribute.lineHeight * 2) {
this.textModifier.needProcess = false;
this.textModifier.title = this.textSectionAttribute.title;
return;
} else {
this.textModifier.needProcess = true;
}
// 根据展开状态处理文本
if (this.expanded) {
this.collapseText();
} else {
this.expandText();
}
}
// 使用二分法精确查找截断位置
public static getShortText(textSectionAttribute: TextSectionAttribute, lastSpan: string): string {
let text = TextUtils.getStringFromResource(textSectionAttribute.title);
const minLinesTextSize: SizeOptions | undefined = uiContext?.getMeasureUtils().measureTextSize({
textContent: text,
fontSize: textSectionAttribute.fontSize,
maxLines: textSectionAttribute.maxLines,
wordBreak: WordBreak.BREAK_ALL,
constraintWidth: textSectionAttribute.constraintWidth
});
const minHeight: Length | undefined = minLinesTextSize?.height;
if (minHeight === undefined) return '';
let textStr: string[] = Array.from(text);
let leftCursor: number = 0;
let rightCursor: number = textStr.length;
let cursor: number = Math.floor(rightCursor / 2);
// 二分查找最优截断点
while (Math.abs(rightCursor - leftCursor) > 1) {
let tempTitle = text.substring(0, cursor) + suffix + lastSpan;
const currentLinesTextSize = uiContext?.getMeasureUtils().measureTextSize({
textContent: tempTitle,
fontSize: textSectionAttribute.fontSize,
wordBreak: WordBreak.BREAK_ALL,
constraintWidth: textSectionAttribute.constraintWidth
});
if (currentLinesTextSize?.height > minHeight) {
rightCursor = cursor; // 超过行高,向左查找
} else {
leftCursor = cursor; // 未超过行高,向右查找
}
cursor = leftCursor + Math.floor((rightCursor - leftCursor) / 2);
}
return text.substring(0, cursor) + suffix;
}
二、 富文本展开折叠:复杂内容的高级处理
场景挑战
- 文本中包含表情图片、不同颜色字号
- 关键字带有超链接功能
- 图片位置和大小不固定,截断计算复杂

实现原理
利用HarmonyOS的graphics.text模块进行精确的文本排版计算,通过坐标转换确定截断位置。
核心实现步骤
1. 初始化文本排版环境
// 创建段落样式
let myParagraphStyle: text.ParagraphStyle = {
textStyle: {
fontSize: uiContext?.fp2px(fontSize) // 注意单位转换
},
align: text.TextAlign.START,
maxLines: 300, // 设置足够大的行数进行预排版
breakStrategy: text.BreakStrategy.GREEDY,
wordBreak: text.WordBreak.BREAK_WORD
};
let fontCollection = new text.FontCollection();
let paragraphGraphBuilder = new text.ParagraphBuilder(myParagraphStyle, fontCollection);
2. 混合处理文本和图片
// 遍历富文本内容
for (let item of textArray) {
if (item.type === 'image') {
// 添加图片占位符
paragraphGraphBuilder.addPlaceholder({
width: item.imgWidth,
height: item.imgHeight,
align: text.PlaceholderAlignment.BOTTOM_OF_ROW_BOX,
baseline: text.TextBaseline.IDEOGRAPHIC,
baselineOffset: 0
});
} else if (item.type === 'text') {
// 设置文本样式
paragraphGraphBuilder.pushStyle({
fontSize: fontSize,
color: item.color // 支持不同颜色
});
paragraphGraphBuilder.addText(item.content);
paragraphGraphBuilder.popStyle(); // 恢复默认样式
}
}
3. 预排版与截断计算
// 执行排版计算
let paragraph = paragraphGraphBuilder.build();
paragraph.layoutSync(textMaxWidth); // 与实际显示宽度一致
// 计算截断位置的Y坐标
let y = 0;
for (let i = 0; i < textSectionAttribute.maxLines; i++) {
y += (i === textSectionAttribute.maxLines - 1) ?
paragraph.getLineHeight(i) / 2 : paragraph.getLineHeight(i);
}
// 计算截断位置的X坐标
let x = 0;
if (paragraph.getLineWidth(textSectionAttribute.maxLines - 1) + Number(widthMore) > textSectionAttribute.constraintWidth) {
x = textSectionAttribute.constraintWidth - Number(widthMore);
} else {
x = paragraph.getLineWidth(textSectionAttribute.maxLines - 1);
}
// 坐标转换为文本索引
let positionWithAffinity = paragraph.getGlyphPositionAtCoordinate(x, y);
let index = (positionWithAffinity.affinity === text.Affinity.UPSTREAM) ?
positionWithAffinity.position : positionWithAffinity.position + 1;
4. 动态按钮渲染
// 根据状态显示不同按钮
if (this.textModifier.needProcess && !this.textModifier.exceedOneLine) {
// 显示"展开"按钮
Span(this.lastSpanAttribute.content[0])
.fontColor(this.lastSpanAttribute.color)
.onClick(() => {
this.expanded = true;
this.expandText();
})
} else if (this.textModifier.exceedOneLine) {
// 显示"收起"按钮
Span(this.lastSpanAttribute.content[1])
.fontColor(this.lastSpanAttribute.color)
.onClick(() => {
this.expanded = false;
this.collapseText();
})
}
三、 关键技术要点
1. 单位转换一致性
- 排版计算使用px单位
- 显示时进行px2vp转换
- 字体大小考虑系统缩放比例
2. 性能优化策略
- 二分法查找避免全量计算
- 预排版只执行一次
- 缓存计算结果减少重复测量
3. 兼容性处理
- 纯文本降级方案
- 富文本图片容错
- 多语言字符支持
四、 总结
HarmonyOS提供了从简单到复杂的完整文本处理方案:
- 纯文本场景:使用
measureTextSizeAPI快速实现,代码简洁高效 - 富文本场景:借助
graphics.text排版引擎,精准处理混合内容
两种方案都通过精确的数学计算确保截断位置的准确性,为用户提供流畅自然的展开收起体验。开发者可以根据业务复杂度选择合适的实现路径,构建体验优秀的文本展示组件。
这套方案已在实际项目中验证,能够稳定处理各种复杂的文本场景,是构建内容类应用的理想选择。
更多关于HarmonyOS 鸿蒙Next中文本展开折叠:从纯文本到富文本的完整解决方案的实战教程也可以访问 https://www.itying.com/category-93-b0.html
666
更多关于HarmonyOS 鸿蒙Next中文本展开折叠:从纯文本到富文本的完整解决方案的实战系列教程也可以访问 https://www.itying.com/category-93-b0.html
在HarmonyOS鸿蒙Next中,文本展开折叠功能通过Text组件结合自定义布局实现。使用文本插值和条件渲染控制显示内容长度,通过状态变量管理展开/折叠状态。富文本支持通过Span组件添加样式和交互,结合Click事件处理展开折叠逻辑。布局自动适配文本内容变化,确保UI流畅性。系统提供文本测量接口精确计算截断位置,避免字符截断问题。
这篇文章详细介绍了HarmonyOS Next中文本展开折叠功能的完整实现方案,技术深度和实用性都很强。
纯文本部分通过measureTextSizeAPI结合二分法精确计算截断位置,代码简洁高效。富文本处理则充分利用了graphics.text排版引擎,通过坐标转换处理混合内容,解决了图片、不同样式等复杂场景的截断难题。
特别值得肯定的是文中提到的关键技术要点:单位转换一致性、性能优化策略和兼容性处理,这些都是实际开发中容易忽略但至关重要的细节。二分法查找和预排版缓存的优化策略能有效提升性能表现。
这套方案从简单到复杂覆盖了完整的应用场景,为HarmonyOS开发者提供了很好的参考实现。

