Compose Multiplatform Windows 中文变方框:一套按成本分层的完整修复指南
【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform
Compose Multiplatform 桌面应用在 Windows 上运行时中文变成"方框"?本文定位根因——默认字体不含中文字形——并按实施成本给出三档修复手段,结尾附可直接执行的清单。
症状自查:中文显示异常的三种形态
先别急着改代码,症状能告诉你问题有多严重:
- 整段文字变成"豆腐块"方框:当前使用的字体里根本没有中文字形,最严重,必须处理;
- 中文比英文明显偏小、行距跳变:框架回退到了某个系统字体来渲染中文,但字宽字高指标对不上,看着"发飘";
- 只有 emoji 和个别特殊符号异常:主字体没问题,缺的是回退链。
三种情况一条都不沾的话,说明你项目的字体配置基本健康,可以跳过了。
根因:默认的 Roboto 字体为什么装不下中文
Compose Multiplatform 内嵌的默认字体是 Roboto——在 CHANGELOG.md 里搜 "embedded font" 能看到切换为 Roboto 的记录。Roboto 只覆盖拉丁字母和少数邻近文字,字符集里没有中文。
于是链条是:桌面渲染层向字体要字形 → 查无此字 → 尝试系统回退 → 仍找不到中文字体 → 输出"方框"占位符。
所以这不是渲染 bug,而是"找不到中文字体"的问题。下面所有手段都围绕一件事:把一份可靠的中文字体送到应用手里。
零成本档:按名字引用 Windows 系统中文字体
如果只在自己 Windows 开发机上跑,或目标机器都装了雅黑,可以省掉一切打包工作——直接按名字引用系统字体,不放任何字体文件。这条路是官方支持的(CHANGELOG.md 中 "use fonts installed on the system" 条目即此功能的引入记录)🔍
// desktopMain/kotlin/app/Fonts.kt // 系统字体族直接按名字引用,零包体增量 // 列表顺序就是回退顺序:先雅黑,找不到再宋体 val WindowsCnFont = FontFamily(listOf("Microsoft YaHei", "SimSun"))然后把字体族用到你的文本样式里(写法和下一档完全一致,只是字体来源不同)。这个档位的优点是零增量、五分钟见效;缺点是用户的机器你控制不了——换了字体或者装了精简版系统,你就抓瞎。所以它适合开发期快速验证,正式发布的场景建议直接上下一档。
一次打包档:把中文字体打进应用(推荐)
这是最稳的做法:字体随应用走,任何一台 Windows 都渲染一致。
第 1 步:准备字体资源。把中文字体文件(ttf/otf)放进commonMain/composeResources/font/目录,框架的资源规范会自动识别该目录并生成访问入口,不用手工注册。仓库里的 codeviewer 示例项目 就是这么做的,全部 JetBrains Mono 字体文件都在shared/src/commonMain/composeResources/font/下,目录结构可以照抄。
第 2 步:定义字体族。资源引用名取"去掉扩展名的文件名",横杠写成形参:
// commonMain/kotlin/app/Fonts.kt // 字体放入 composeResources/font 后自动生成 Res.font 访问器 val CnFont = FontFamily( Font(Res.font.source_han_sans), // 常规字重 Font(Res.font.source_han_sans_bold, weight = FontWeight.Bold) // 粗体 )第 3 步:全局套用。把字体族写进主题 typography 的各层级,Text 就会自动生效:
MaterialTheme( typography = Typography( titleLarge = TextStyle(fontFamily = CnFont, fontSize = 28.sp), bodyLarge = TextStyle(fontFamily = CnFont, fontSize = 16.sp) ) ) { content() }两个真实会踩的坑:① 完整中文字体动辄 5~10MB,用字体子集化工具压缩;② 常规 + 粗体建议都带上,否则粗体标题走伪粗体渲染,发虚。
进阶档:Web 目标字体预加载与回退策略
如果项目还面向 Web/Wasm 目标,还有一个独立的问题:首屏时字体文件还没下载完,会先闪现几秒回退字体甚至方框。CHANGELOG.md 中新增的preloadFontAPI 就是干这个的——提前把字体资源缓存下来,避免首次加载"字体跳变"🌐
// jsMain 目标:提前预加载,避免首屏用回退字体渲染 suspend fun preloadCnFont() { preloadFont("font/source_han_sans.ttf") }桌面端如果不想放弃系统字体,也可以做混合:把系统字体族和内置字体族放进同一条回退链,框架按顺序尝试,有雅黑用雅黑,没有就用你带的那份。
验证修复:高 DPI 与跨平台比对三步走
- 分别在 Windows 10、11 上运行,切换 100% / 150% / 200% 缩放——高 DPI 是中文显示异常的重灾区,正文、标题、输入框都要看一遍;
- 和其他平台对比效果。字体统一后,多端文本渲染应当保持一致:
- 若"改了字体不生效",大概率是字体缓存在起作用:框架会缓存解析后的字体数据(CHANGELOG.md 中这条优化就是为了避免每次使用都重复读取字体字节)。开发期重启应用即可,改过资源路径就重新构建刷新缓存。
常见问题速查
| 现象 | 最可能原因 | 处理动作 |
|---|---|---|
| 全部文字方框 | 没有覆盖中文字形的字体 | 按"一次打包档"内置字体 |
| 改了字体不生效 | 资源路径错 / 缓存未刷新 | 确认文件在composeResources/font,重建并重启 |
| 包体异常增大 | 未压缩的完整字体 | 字体子集化,单文件控制在 5MB 内 |
| 仅 emoji 异常 | 回退字体缺失 | 补一份支持 emoji 的字体或预加载回退字体 |
| 中文行距不齐 | 缺粗体、指标不一致 | 补粗体字重,常规/粗体用同一字体族 |
其余疑问可查 docs/FAQ.md。
行动清单(按顺序执行)
- 对照症状自查确定你属于哪种异常,选择对应档位;
- 开发期:
FontFamily(listOf("Microsoft YaHei", "SimSun"))引系统字体,先跑通; - 发布前:常规 + 粗体中文字体放入
commonMain/composeResources/font/,重建FontFamily; - 把字体族应用到
Typography全部层级; - 有 Web 目标就补上
preloadFont预加载; - 用三步验证:高 DPI 缩放、跨端对比、重建刷新缓存。
六步走完,Windows 上的中文显示问题就闭环了。
【免费下载链接】compose-multiplatformCompose Multiplatform, a modern UI framework for Kotlin that makes building performant and beautiful user interfaces easy and enjoyable.项目地址: https://gitcode.com/GitHub_Trending/co/compose-multiplatform
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考