📝 React Native 自定义字体导致 Text / TextInput 文本垂直不居中的终极解决方案
适用
✔ React Native(0.71+)
✔ Expo(所有版本)
✔ iOS + Android
✔ 自定义字体(TTF / OTF)
📌 一、问题背景
当项目使用自定义字体(从设计师那拿的 TTF/OTF)时,Text 和 TextInput 常会出现这些问题:
- 文字偏上 / 偏下
- placeholder 和输入的文字不对齐
- lineHeight 设置了但不生效
- iOS 看似正常,Android 完全偏
- 同一个字体在不同组件间高度不一致
这些问题在系统字体下不会出现,但是换成自定义字体后几乎必现。
📌 二、为什么会发生?------关键因素是 字体度量(Font Metrics)
每个字体内部都带有一套关键数据:
- ascent:字体向上的高度
- descent:字体向下的高度
- lineGap:字体推荐的行间距
- unitsPerEm:字体的"单位体系",决定比例
- baseline:文字真实摆放的位置
React Native 的 Text / TextInput 会根据这些 metrics 来排版文字。
而自定义字体常常存在:
- ascent 特别高
- descent 太大
- lineGap > 0(设计师字体常见)
- unitsPerEm 不是系统常用的 1000 / 2048
- baseline 偏移
🔍 结果:文字在组件内部的位置发生偏移
为什么系统字体没问题?
因为系统字体(San Francisco / Roboto)对移动端做过特殊优化,而自定义字体没有。
📌 三、平台差异(非常关键)
iOS:比较宽容
- 会自动平滑 baseline
- 会人为修正部分 ascent/descent
- 所以问题不明显
Android:非常严格
- 绝对使用字体真实 fontMetrics
- lineHeight 必须由 dev 明确指定
- 不自动居中 TextInput 文字
👉 所以大部分"文字偏上"的问题都出现在 Android。
📌 四、终极解决方案(按效果排序)
✅ 方案 1:为自定义字体设置合理的 lineHeight(最推荐)
yaml
<TextInput
style={{
fontFamily: "YourFont",
fontSize: 16,
lineHeight: 20, // 常用: fontSize * 1.25
}}
/>
经验公式:
iOS:
ini
lineHeight = fontSize * 1.2
Android:
ini
lineHeight = fontSize * 1.3
👉 这是能解决 90% 自定义字体问题的方案。
✅ 方案 2:给 TextInput 外面套一层 View 并控制垂直居中
因为 TextInput 自己不擅长对齐 ------ 尤其是 Android。
less
<View style={{ height: 48, justifyContent: "center" }}>
<TextInput
style={{
fontFamily: "YourFont",
fontSize: 16,
lineHeight: 20,
padding: 0,
textAlignVertical: "center", // Android 必须加
}}
/>
</View>
⚠️ 关键点:
- padding 必须设为 0(否则 Android 会额外加)
- textAlignVertical=center(Android 不加就会偏)
✅ 方案 3:统一 placeholder 与文字行高
React Native 的 placeholder 不跟随文字行高(尤其 Android)。
强制同步:
ini
placeholderTextColor="#888"
并保持同样高度的 wrapper:
less
<View style={{ height: 48, justifyContent: "center" }}>
<TextInput
// same styles as above
/>
</View>
🔧 方案 4(重量但最彻底):修复字体文件的 metrics
如果一个字体怎么调都不对齐,那它就是 本身度量有问题。
可以用字体编辑器修:
- FontForge(免费)
- Glyphs(macOS)
- FontTools(CLI)
需要修的字段:
- ascent → 调整到合理比例
- descent → 调整到合理比例
- lineGap → 设为 0(移动端最佳实践)
- unitsPerEm → 1000 或 2048(常用规范)
导出后 RN 的 Text/TextInput 高度立即正常。
📌 五、最佳实践组件(可直接复制到项目)
javascript
import { Platform, View, TextInput } from "react-native";
export function Input(props) {
const lineHeight = Platform.OS === "android" ? 22 : 20;
return (
<View style={{ height: 48, justifyContent: "center" }}>
<TextInput
{...props}
style={[
{
fontFamily: "YourFont",
fontSize: 16,
lineHeight,
padding: 0,
textAlignVertical: "center",
},
props.style,
]}
/>
</View>
);
}
📌 六、快速判断是哪种问题(排查表)
| 现象 | 原因 | 解决方案 |
|---|---|---|
| 文字偏上 | ascent 太大 | 增加 lineHeight |
| placeholder 偏位置 | TextInput baseline 变化 | 包 wrapper |
| Android 完全不对齐 | 不支持自动 baseline | 加 textAlignVertical |
| iOS 正常、Android 不正常 | Android 用真实 metrics | 统一 lineHeight |
| 不同设备看起来不一样 | 字体 degree scaling 不一致 | wrapper + lineHeight |
| 怎么调都不对 | 字体文件本身错误 | 修字体 metrics |
📌 七、总结
React Native 使用自定义字体后高度/对齐异常 不是 RN 的 bug,而是:
- 自定义字体度量不规范
- RN 必须遵守字体真实 metrics
- Android 更严格
- TextInput 本身对行高处理简单
最有效的办法是:
- 统一 lineHeight
- 外包 wrapper
- Android 加 textAlignVertical=center
- 不行就 修字体度量