鸿蒙开发-想做高级文本排版?text文本模块的排版和字体管理
高级文本排版text 模块帮你搞定一切你有没有遇到过这样的需求在一个自定义绘制的界面上需要精确控制文字的排版比如做一个电子书阅读器需要支持各种字体、字号、行间距或者做一个图片编辑器需要在图片上叠加文字还要控制文字的位置和样式。HarmonyOS 的ohos.graphics.text模块就是干这个的。它提供了一套完整的文本排版和字体管理 API能让你在自定义绘制场景中实现高质量的文本渲染。导入模块import{text}fromkit.ArkGraphics2D核心概念从字体到段落下面是 text 模块排版的整体流程获取字体集 FontCollection加载自定义字体定义文本样式 TextStyle定义段落样式 ParagraphStyle创建段落构建器 ParagraphBuilder添加文字内容 addText构建段落对象 build执行排版 layoutSync查询结果或绘制到画布在开始写代码之前先理解一下这个模块的核心概念FontCollection字体集管理所有可用的字体包括系统字体和自定义字体。TextStyle文本样式定义文字的外观比如字体、字号、颜色、字重等。ParagraphStyle段落样式定义段落的排版规则比如对齐方式、断行策略等。ParagraphBuilder段落生成器用来构建段落对象的工具你可以往里面添加文字和样式。Paragraph段落最终的排版结果可以测量尺寸、绘制到画布上。打个比方如果把排版比作盖房子FontCollection是你的建材库有哪些砖、哪些瓦TextStyle是装修方案用什么材料、什么颜色ParagraphStyle是建筑规范房间怎么布局ParagraphBuilder是施工队按照方案和规范来盖Paragraph是盖好的房子可以测量面积、可以展示第一步获取字体集字体集是排版的基础你得先告诉系统我能用哪些字体。获取全局字体集import{text}fromkit.ArkGraphics2DfunctiontextFunc(){letfontCollectiontext.FontCollection.getGlobalInstance();}EntryComponentstruct Index{fun:FunctiontextFunc;build(){Column(){Button().onClick((){this.fun();})}}}FontCollection.getGlobalInstance()返回应用全局的字体集实例。这个实例在整个应用生命周期内都有效包含了系统预装的所有字体。获取本地字体集推荐卡片场景import{text}fromkit.ArkGraphics2DletfontCollectiontext.FontCollection.getLocalInstance();如果你在做 ArkTS 卡片建议用getLocalInstance()它会创建一个独立的字体集实例不会影响主应用。第二步加载自定义字体系统自带的字体可能满足不了你的需求。比如你想用一个特殊的艺术字体来做标题就需要加载自定义字体。同步加载import{text}fromkit.ArkGraphics2DletfontCollection:text.FontCollectionnewtext.FontCollection();EntryComponentstruct RenderTest{LoadFontSyncTest(){fontCollection.loadFontSync(Clock_01,file:///system/fonts/HarmonyClock_01.ttf)letfontFamilies:Arraystring[Clock_01]letmyTextStyle:text.TextStyle{fontFamilies:fontFamilies};letmyParagraphStyle:text.ParagraphStyle{textStyle:myTextStyle,}letparagraphBuilder:text.ParagraphBuildernewtext.ParagraphBuilder(myParagraphStyle,fontCollection);lettextData测试 loadFontSync 加载字体HarmonyClock_01.ttf;paragraphBuilder.addText(textData);letparagraph:text.ParagraphparagraphBuilder.build();paragraph.layoutSync(600);}aboutToAppear(){this.LoadFontSyncTest();}build(){}}loadFontSync的第一个参数是字体的别名随便取第二个参数是字体文件的路径。加载后在TextStyle的fontFamilies里用这个别名就能使用这个字体了。异步加载import{text}fromkit.ArkGraphics2DletfontCollection:text.FontCollectionnewtext.FontCollection();EntryComponentstruct RenderTest{asyncloadFontPromise(){fontCollection.loadFont(testName,file:///system/fonts/a.ttf).then((data){console.info(Succeeded in doing loadFont${JSON.stringify(data)});}).catch((error:Error){console.error(Failed to do loadFont, error:${JSON.stringify(error)}message:${error.message});});}aboutToAppear(){this.loadFontPromise();}build(){}}异步加载不会阻塞 UI 线程适合加载大字体文件的场景。第三步构建段落有了字体集就可以开始构建段落了。letfontCollection:text.FontCollectiontext.FontCollection.getGlobalInstance();// 定义文本样式letmyTextStyle:text.TextStyle{fontFamilies:[HarmonyOS Sans],fontSize:24,fontWeight:text.FontWeight.W400,color:{alpha:255,red:0,green:0,blue:0}};// 定义段落样式letmyParagraphStyle:text.ParagraphStyle{textStyle:myTextStyle,align:text.TextAlign.LEFT,maxLines:5};// 创建段落构建器letparagraphBuilder:text.ParagraphBuildernewtext.ParagraphBuilder(myParagraphStyle,fontCollection);// 添加文字paragraphBuilder.addText(这是一段测试文字用来演示 text 模块的排版功能。);// 构建段落letparagraph:text.ParagraphparagraphBuilder.build();// 设置排版宽度并执行排版paragraph.layoutSync(600);这段代码做了以下几件事定义了一个TextStyle设置了字体、字号、字重和颜色。定义了一个ParagraphStyle设置了文本样式、对齐方式和最大行数。创建了一个ParagraphBuilder传入段落样式和字体集。用addText添加文字内容。调用build()构建段落对象。调用layoutSync(600)设置排版宽度为 600 像素并执行排版。第四步查询排版结果段落排版完成后你可以查询各种排版信息paragraph.getHeight()获取段落总高度paragraph.getLongestLine()获取最长一行的宽度paragraph.getLineCount()获取行数paragraph.getTextLines()获取所有行对象TextLine每一行TextLine又可以进一步获取textLine.getGlyphRuns()获取这一行的所有排版单元Run每个 Run 包含了连续的、使用相同样式的文字片段。通过 Run 你可以获取每个字符的位置信息这在做文字选中、文字点击等交互时非常有用。字体管理查询系统字体系统字体和自定义字体的加载方式有所不同下面是字体加载的决策流程系统字体自定义字体文件资源引用字体阻塞加载非阻塞加载需要使用字体字体来源?getSystemFontFullNamesByType 查询加载方式?loadFontSync 同步加载loadFontSyncloadFont 异步加载获取字体描述符 FontDescriptor在 TextStyle 中使用字体别名除了加载自定义字体你还可以查询系统已有的字体。按类型获取字体名称列表import{text}fromkit.ArkGraphics2Dimport{BusinessError}fromkit.BasicServicesKitEntryComponentstruct Index{build(){Row(){Column(){Button(get font list).fontSize(30).fontWeight(FontWeight.Bold).width(300).height(80).onClick((){letfontType:text.SystemFontTypetext.SystemFontType.GENERICletpromisetext.getSystemFontFullNamesByType(fontType)promise.then((data){console.info(then font list size:${data.length})data.forEach((fontItem){console.info(fontItem)})}).catch((error:BusinessError){console.error(Failed to get font fullNames by type, error:${JSON.stringify(error)});});})}.width(100%)}.height(100%)}}根据字体名称获取字体描述符import{text}fromkit.ArkGraphics2Dimport{BusinessError}fromkit.BasicServicesKitEntryComponentstruct Index{build(){Row(){Column(){Button(get fontDescriptor).fontSize(30).fontWeight(FontWeight.Bold).width(300).height(80).onClick((){letfontType:text.SystemFontTypetext.SystemFontType.GENERICletpromisetext.getFontDescriptorByFullName(HarmonyOS Sans,fontType)promise.then((fontDescriptor){console.info(desc:${JSON.stringify(fontDescriptor)})}).catch((error:BusinessError){console.error(Failed to get fontDescriptor by fullName, error:${JSON.stringify(error)});});})}.width(100%)}.height(100%)}}字体描述符FontDescriptor包含了字体的详细信息路径、名称、字重、宽度、是否斜体、是否等宽等。你可以用它来判断某个字体是否满足你的需求。匹配字体描述符import{text}fromkit.ArkGraphics2Dimport{BusinessError}fromkit.BasicServicesKitEntryComponentstruct Index{build(){Row(){Column(){Button(font descriptor).fontSize(30).fontWeight(FontWeight.Bold).width(300).height(80).onClick((){console.info(Get font descriptor start)letpromisetext.matchFontDescriptors({weight:text.FontWeight.W400,})promise.then((data){console.info(Font descriptor array size:${data.length});console.info(Font descriptor result:${JSON.stringify(data)})}).catch((error:BusinessError){console.error(Failed to match the font descriptor, error:${JSON.stringify(error)});});})}.width(100%)}.height(100%)}}matchFontDescriptors可以根据你指定的条件比如字重、是否等宽等来搜索系统中符合条件的字体。这在做一个字体选择器功能时特别有用。全局配置高对比度文字和未定义字形设置文字高对比度模式text.setTextHighContrast(text.TextHighContrast.TEXT_APP_DISABLE_HIGH_CONTRAST)这个设置会影响整个进程的文字渲染。如果你的 APP 面向视力不好的用户可以开启高对比度模式让文字更清晰。设置未定义字形的显示方式text.setTextUndefinedGlyphDisplay(text.TextUndefinedGlyphDisplay.USE_TOFU)当字体中没有某个字符的字形时默认行为是用字体内部的 .notdef 字形可能是空框或空格。如果你设置成USE_TOFU就会强制用豆腐块方框来显示方便你在开发阶段发现缺失的字符。小结text模块是一个功能非常完整的文本排版引擎。它的核心流程是获取或创建字体集FontCollection加载需要的字体loadFontSync / loadFont定义文本样式和段落样式用 ParagraphBuilder 构建段落调用 layoutSync 排版查询排版结果或绘制到画布如果你的 APP 需要在自定义绘制场景中处理文本——不管是做电子书阅读器、图片编辑器、还是自定义 UI 组件——这个模块都能满足你的需求。