Typeface — 是来自 android.graphics 包的一个 Android SDK 类,表示字体的外观,用于在 TextView 和其他 UI 组件中自定义文本。Typeface 定义字体的家族、粗细(normal, bold)和样式(normal, italic)。根据 Google Developers,Typeface 支持从 TTF、OTF 文件、XML 字体和系统资源加载。这是在 Android 应用程序中管理排版的核心类。
要点
Typeface — 是一个 Android 类,封装了关于字体的信息:它的家族(Roboto, Arial, Serif)和样式修饰符(粗体、斜体)。与 iOS 中的 UIFont 不同,Typeface 不包含尺寸信息 — 文本大小通过 TextView 中的 textSize 属性或 Spannable 中的 TextStyle 单独设置。Typeface 只负责字体的外观和风格。
该类在所有 API 级别可用。直到 Android 4.4,Typeface 只支持有限的系统字体集:DEFAULT、DEFAULT_BOLD、MONOSPACE、SERIF、SANS_SERIF。从 Android 8.0(API 26)开始,添加了通过 XML 资源加载自定义字体的支持,Android 10 增加了对可变字体的支持。
Android 提供了几个内置的 Typeface 实例:Typeface.DEFAULT — 系统字体 Roboto(Material)或 Noto(AOSP),DEFAULT_BOLD — 粗体版本,MONOSPACE — 用于代码的等宽字体,SERIF — 衬线字体(Noto Serif),SANS_SERIF — 无衬线字体(Noto Sans)。这些常量无需上下文即可使用,不需要文件读取权限。
Android 系统使用 Typeface 来渲染所有文本,包括系统 UI、标题、标签和通知。每个应用程序可以通过 主题(android:fontFamily)或通过 setTypeface() 以编程方式覆盖默认字体。Typeface 应用于所有继承自 TextView 的组件:按钮、EditText 和自定义视图。
Android 8.0+ 允许向 res/font 添加字体并通过 XML 资源使用它们。字体文件(TTF 或 OTF)放置在 res/font 中,然后创建 R.font.font_name 引用。Typeface 通过 ResourcesCompat.getFont(context, R.font.font_name) 加载。这种方法更可取,因为字体被编译到 APK 中,支持 ProGuard 布局系统。
资源方法还通过 res/font 中的 XML 文件支持 字体家族(font family):可以为不同的外观(regular、bold、italic)指定多个文件,系统会在调用 setTypeface 时自动选择合适的文件。这简化了排版 — 只需在主题中设置 fontFamily,Android 会自动选择正确的文件。
Typeface.create(Typeface family, int style) 方法使用指定的家族和样式创建字体实例。如果家族已经具有所请求的样式,则返回现有实例。如果样式不存在,Android 会合成它 — 对基础字体应用伪粗体或人工斜体。
val boldTypeface = Typeface.create(Typeface.SANS_SERIF, Typeface.BOLD)
val italicTypeface = Typeface.create(Typeface.MONOSPACE, Typeface.ITALIC)
val customTypeface = ResourcesCompat.getFont(context, R.font.montserrat_regular)
textView.typeface = boldTypeface
合成的 伪粗体 在细字体上可能看起来质量不佳。为了获得更好的效果,请使用具有真实粗体外形的字体文件。Typeface.create() 对于不存在的样式不会抛出异常,而是返回最接近的可用变体。
要从 assets 加载 Typeface,请使用 Typeface.createFromAsset(assetManager, path)。字体放置在 src/main/assets/fonts/ 目录中,路径相对于 assets 指定。此方法适用于所有 Android 版本,但不支持字体家族,并且需要为每种外观指定具体文件。
Typeface.createFromFile(File) 方法从文件系统中的任意文件加载字体。这对于从网络下载或复制到应用程序内部存储的字体非常有用。createFromFile 也不需要上下文,这对于后台任务很方便,但文件必须可读。
通过 createFromAsset 从 assets 重复加载 Typeface 可能会导致创建重复对象。每次调用 createFromAsset 都会重新加载文件。建议通过 HashMap 实现 Typeface 缓存,其中键是文件路径,值是加载的实例。这对于具有不同字体类型的列表尤为重要。
object TypefaceCache {
private val cache = mutableMapOf<String, Typeface>()
fun getTypeface(context: Context, filename: String): Typeface {
return cache.getOrPut(filename) {
Typeface.createFromAsset(context.assets, "fonts/$filename")
}
}
}
// 使用
val typeface = TypefaceCache.getTypeface(context, "Roboto-Bold.ttf")
内存中的缓存确保一种字体在每个应用程序会话中 只加载一次。在配置更改(屏幕旋转)时,缓存会保留,因为它位于 companion 对象中。要在内存不足时清除缓存,请使用 WeakHashMap 或监听 onTrimMemory。
Typeface 通过 setTypeface(Typeface tf) 或 setTypeface(Typeface tf, int style) 方法应用于 TextView。第二种变体允许相对于当前字体更改样式(bold/italic)。在 XML 中,使用 android:fontFamily 属性引用字体或系统家族。对于编程设置,调用 setTypeface(monospaceTypeface) 会将字体替换为指定的字体。
Typeface 影响整个 TextView 文本。如果在同一行中需要不同的字体,可以使用带有 TypefaceSpan 的 SpannableString。TypefaceSpan 接受 Typeface 或 family,并应用于字符范围。这是在单个 TextView 内混合字体而无需嵌套组件的唯一方法。
仅将 TypefaceSpan 应用于字符串的一部分。例如,文本中的变量值应该用等宽字体显示,其余文本用系统字体。创建一个 SpannableString,在所需范围上设置带有 MONOSPACE Typeface 的 TypefaceSpan。文本的其余部分保持 TextView 的原始字体。
val text = "Code: variableName"
val spannable = SpannableString(text)
spannable.setSpan(
TypefaceSpan(Typeface.MONOSPACE),
5, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
spannable.setSpan(
ForegroundColorSpan(Color.BLUE),
5, text.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
)
textView.text = spannable
文本 “variableName” 将以蓝色等宽字体显示,而单词 “Kod:” 将保持系统字体。TypefaceSpan 可以与其他 spans 组合,以精确控制文本的每个部分。
Android 支持多种字体格式:TrueType (TTF)、OpenType (OTF) 和从 Android 10 开始的 Web Open Font Format (WOFF2)。TTF 和 OTF 是自定义字体的主要格式。XML 字体(Font Family)不是单独的格式,而是用作描述,说明哪种文件用于哪种外观。
要通过 res/font 使用自定义字体,请创建一个 XML 家族文件,其中指定 regular、bold 和 italic 字体的文件路径。然后在应用程序的主题中设置 android:fontFamily="@font/custom_family"。所有使用此主题的 TextView 将自动获得每种样式的正确外观。
如果字体从网络下载,请将其保存在 filesDir 或 cacheDir 中,并通过 Typeface.createFromFile 加载。加载后,更新 TextView 中的 Typeface。对于异步加载,使用 coroutine 或 DownloadManager,完成后通过 Handler 发布到 UI 线程。这是具有按需加载的自定义排版的应用程序的典型方案。
// 将字体文件保存到内部存储
val fontFile = File(context.cacheDir, "downloaded-font.otf")
fontFile.outputStream().use { output ->
// 来自网络的输入流
inputStream.copyTo(output)
}
// 从文件创建 Typeface
val downloadedTypeface = Typeface.createFromFile(fontFile)
textView.typeface = downloadedTypeface
在加载之前,检查文件的可用性:如果文件损坏或 无效,Typeface.createFromFile 将抛出 RuntimeException。建议将调用包装在 try-catch 中,并在出错时保留默认的系统字体。
在 Jetpack Compose 中,Typeface Android SDK 不直接使用。相反,Compose 为排版提供 FontFamily 和 FontWeight。FontFamily 通过 Font.Resource 和 Font.File 加载。Compose 的排版系统通过 MaterialTheme.typography 设置,其中可以为每个样式(h1、body1、button)分配自定义的 FontFamily。
要从 resources 加载字体,Compose 使用 Font(R.font.font_name, weight, style)。来自 assets 的字体通过 Font(assetPath = "fonts/custom.otf", weight, style) 加载。定义字体后,将其传递给 Typography,然后通过 MaterialTheme.typography.bodyLarge 和类似样式应用。
如果存在通过 Android SDK 创建的现成 Typeface,可以在 Compose 中通过 TypefaceAdapter 或基于 Typeface 创建 FontFamily 来使用它。为此,从 Typeface.create 获取 Typeface 并将其传递给 FontFamily.Custom(listOf(Font(typeface)))。但是,建议使用带有 Font.Resource 的原生方法,因为它针对 Compose 进行了更好的优化。
从文件加载字体是一种潜在的开销较大的操作。每次调用 Typeface.createFromAsset 或 Typeface.createFromFile 都会从磁盘读取文件,解析它并创建 Typeface 对象。对于 1-2 MB 的字体,冷启动需要 10-50 毫秒。对于具有自定义字体的应用程序,Typeface 缓存是必需的实践。
Android SDK 不会缓存通过 createFromAsset 或 createFromFile 创建的字体。与在 Zygote 过程中预加载的系统字体不同,自定义字体在每次访问时加载。建议使用 LruCache 或 ConcurrentHashMap,键为字体标识符。缓存大小 — 10-20 个实例,涵盖了应用程序的典型字体集。
Android 8.0+ 通过 XML Font Family 优化字体加载。在 XML 布局中指定 android:fontFamily="@font/my_family" 时,Android 会惰性加载字体 — 仅在组件变得可见时。系统还会在进程的全局缓存中缓存通过 ResourcesCompat.getFont() 加载的字体。这使得资源方法最具性能。
在 RecyclerView 适配器中使用 Typeface 时,重要的是不要在 onBindViewHolder 方法中加载字体。在创建适配器时一次性加载,或使用预加载了 Typeface 的 ViewHolder。如果列表中的每个项都需要独特的字体,请通过 WeakHashMap 实现缓存,并在滚动到可见位置之外时清除它以节省内存。
常见问题
Typeface — 是用于编程使用的具体字体实例。FontFamily — 是一种 XML 资源,描述用于不同外观(regular、bold、italic)的一组字体文件。在运行时,FontFamily 通过 ResourcesCompat.getFont 转换为 Typeface。Typeface 可以直接从文件创建,绕过 FontFamily。
使用 Typeface.createFromFile(File) 或 Typeface.DEFAULT。这些方法不需要上下文,因为它们与文件系统或内置常量一起使用。要从 assets 或资源加载,需要上下文,因为需要访问 AssetManager 和应用程序资源。
不可以,WebView 使用 CSS font-family 属性来指定字体。要在 WebView 中使用自定义字体,将字体文件放在 assets 中,并通过 CSS 中的 @font-face 指定。Typeface Android SDK 仅适用于原生 UI 组件(TextView 及其派生类)。
是的,从 Android 10(API 29)开始。可变字体通过 ResourcesCompat.getFont 或 Typeface.createFromFile 加载。要调整可变性轴(weight、width),可以使用 Compose 中的 FontStyle 或 XML 属性。以编程方式更改轴只能通过底层 Skia API 实现。
最常见的原因:项目中缺少字体文件、文件名错误、字体损坏或使用了文件中不存在的 合成样式。检查文件是否已添加到 res/font 或 assets,以及在资源方法中其名称是否未指定扩展名。
总结
我们将开发一款交钥匙移动应用程序
IT Sectr自2017年以来为初创企业和企业打造iOS和Android应用程序。我们将为您提供咨询并提出最佳解决方案。