Android字体系列 (二):Typeface完全解析

您所在的位置:网站首页 Android字体 Android字体系列 (二):Typeface完全解析

Android字体系列 (二):Typeface完全解析

#Android字体系列 (二):Typeface完全解析| 来源: 网络整理| 查看: 265

这是我参与更文挑战的第 2 天,活动详情查看: 更文挑战

前言

很高兴遇见你~

在本系列的上一篇文章中,我们介绍了关于 Android 字体的一些基础知识,还没有看过上一篇文章的朋友,建议先去阅读 Android字体系列 (一):Android字体基础,你会发现,我们设置的那三个属性最终都会去构建一个 Typeface 对象,今天我们就好好的来讲讲它

注意:本文所展示的系统源码都是基于Android-30 ,并提取核心部分进行分析

一、Typeface 介绍

Typeface 负责 Android 字体的加载以及对上层提供相关字体 API 的调用

如果你想要操作字体,无论是使用 Android 系统自带的字体,还是加载自己内置的 .ttf(TureType) 或者 .otf(OpenType) 格式的字体文件,你都需要使用到 Typeface 这个类。因此我们要全局修改字体,首先就要把 Typeface 给弄明白

二、Typeface 源码分析

源码分析环节可能比较枯燥,坚持就是胜利 ⛽️

1、Typeface 初始化

Typeface 这个类会在 Android 应用程序启动的过程中,通过反射的方式被加载。点击源码可以看到它里面有一个 static 代码块,它会随着类的加载而加载,并且只会加载一次,Typeface 就是通过这种方式来进行初始化的,如下:

static { //创建一个存放字体的 Map final HashMap systemFontMap = new HashMap(); //将系统的一些默认字体放入 Map 中 initSystemDefaultTypefaces(systemFontMap,SystemFonts.getRawSystemFallbackMap(),SystemFonts.getAliases()); //unmodifiableMap 方法的作用就是将当前 Map 进行包装,返回一个不可修改的Map,如果调用修改方法就会抛异常 sSystemFontMap = Collections.unmodifiableMap(systemFontMap); // We can't assume DEFAULT_FAMILY available on Roboletric. /** * 设置系统默认字体 DEFAULT_FAMILY = "sans-serif"; * 因此系统默认的字体就是 sans-serif */ if (sSystemFontMap.containsKey(DEFAULT_FAMILY)) { setDefault(sSystemFontMap.get(DEFAULT_FAMILY)); } // Set up defaults and typefaces exposed in public API //一些系统默认的字体 DEFAULT = create((String) null, 0); DEFAULT_BOLD = create((String) null, Typeface.BOLD); SANS_SERIF = create("sans-serif", 0); SERIF = create("serif", 0); MONOSPACE = create("monospace", 0); //初始化一个 sDefaults 数组,并预加载好粗体、斜体等一些常用的 Style sDefaults = new Typeface[] { DEFAULT, DEFAULT_BOLD, create((String) null, Typeface.ITALIC), create((String) null, Typeface.BOLD_ITALIC), }; //... }

上述代码写了详细的注释,我们可以发现,Typeface 初始化主要做了:

1、将系统的一些默认字体放入一个 Map 中

2、设置默认的字体

3、初始化一些默认字体

4、初始化一个 sDefaults 数组,存放一些常用的 Style

完成了 Typeface 的初始化,接下来看 Typeface 提供了一系列创建字体的 API ,其中对上层开放调用的有如下几个:

image-20210614130149262.png

下面我们来重点分析这几个方法

2、通过 Typeface 和 Style 获取新的 Typeface

对应上面截图的第一个 API , 看下它的源码:

public static Typeface create(Typeface family, @Style int style) { //判断当前是否设置了 style , 如果没有设置,置为 NORMAL if ((style & ~STYLE_MASK) != 0) { style = NORMAL; } //判断当前传入的 Typeface 是否为空,如果是,置为默认字体 if (family == null) { family = sDefaultTypeface; } // Return early if we're asked for the same face/style //如果当前 Typeface 的 mStyle 属性和传入的 style 相同,直接返回 Typeface 对象 if (family.mStyle == style) { return family; } final long ni = family.native_instance; Typeface typeface; //使用 sStyledCacheLock 保证线程安全 synchronized (sStyledCacheLock) { //从缓存中获取存放 Typeface 的 SparseArray SparseArray styles = sStyledTypefaceCache.get(ni); if (styles == null) { //存放 Typeface 的 SparseArray 为空,新创建一个,容量为 4 styles = new SparseArray(4); //将当前 存放 Typeface 的 SparseArray 放入缓存中 sStyledTypefaceCache.put(ni, styles); } else { //存放 Typeface 的 SparseArray 不为空,直接获取 Typeface 并返回 typeface = styles.get(style); if (typeface != null) { return typeface; } } //通过 native 层构建创建 Typeface 的参数并创建 Typeface 对象 typeface = new Typeface(nativeCreateFromTypeface(ni, style)); //将新创建的 Typeface 对象放入 SparseArray 中缓存起来 styles.put(style, typeface); } return typeface; }

从上述代码我们可以知道:

1、当你设置的 Typeface 和 Style 为 null 和 0 时,会给它们设置一个默认值

注意:这里的 Style ,对应上一篇中讲的 android:textStyle 属性传递的值,用于设定字体的粗体、斜体等参数

2、如果当前设置的 Typeface 的 mStyle 属性和传入的 Style 相同,直接将 Typeface 给返回

3、从缓存中获取存放 Typeface 的容器,如果缓存中存在,则从容器中取出该 Typeface 并返回

4、如果不存在,则创建新的容器并加入缓存,然后通过 native 层创建 Typeface,并把当前 Typeface 放入到容器中

因此我们在使用的时候无需担心效率问题,它会把我们传入的字体进行一个缓存,后续都是从缓存中去拿的

3、通过字体名称和 Style 获取字体

对应上面截图的第二个 API:

public static Typeface create(String familyName, @Style int style) { //调用截图的第一个 API return create(getSystemDefaultTypeface(familyName), style); } //获取系统提供的一些默认字体,如果获取不到则返回系统的默认字体 private static Typeface getSystemDefaultTypeface(@NonNull String familyName) { Typeface tf = sSystemFontMap.get(familyName); return tf == null ? Typeface.DEFAULT : tf; }

1、这个创建 Typeface 的 API 很简单,就是调用它的一个重载方法,我们已经分析过

2、getSystemDefaultTypeface 主要是通过 sSystemFontMap 获取字体,而这个 sSystemFontMap 在 Typeface 初始化的时候会存放系统提供的一些默认字体,因此这里直接取就可以了

4、通过 Typeface 、weight(粗体) 和 italic(斜体) 获取新的 Typeface

对应上面截图的第三个 API

public static @NonNull Typeface create(@Nullable Typeface family, @IntRange(from = 1, to = 1000) int weight, boolean italic) { //校验传入的 weight 属性是否在范围内 Preconditions.checkArgumentInRange(weight, 0, 1000, "weight"); if (family == null) { //如果当前传入的 Typeface 为 null, 则置为默认值 family = sDefaultTypeface; } //调用 createWeightStyle 方法创建 Typeface return createWeightStyle(family, weight, italic); } private static @NonNull Typeface createWeightStyle(@NonNull Typeface base, @IntRange(from = 1, to = 1000) int weight, boolean italic) { final int key = (weight


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3