Android 修改字体,跳不过的 Typeface

在 Android 下使用自定义字体已是一个比较常见的需求了,最近也作了个比较深刻的研究。android

那么按照惯例我又要出个一篇有关 Android 修改字体相关的文章,可是写下来发现内容还挺多的,因此我决定将它们拆分一下,分几篇来详细的讲解(多是五篇)。主要会是一些经常使用的替换字体的方案,最后还会介绍一些全局替换的方案,固然也会包含最新的 『Fonts in XML』的方案。算法

期待你持续关注。数组

本篇是本系列的第二篇,以前已经发布的文章,有兴趣能够先看看。缓存

  • Android 字体修改概述|开篇

1、开篇

若是你想要操做字体,不管是使用 Android 系统自带的字体,仍是加载本身内置的 .ttf(TureType) 或者 .otf(OpenType) 格式的字体文件,你都须要使用到 Typeface 这个类。安全

本文就单独来分析 Typeface 的一些源码细节,本文在本系列中,可能相对枯燥一些,可是我以为它又是不可或缺的一部分,因此单独拿出一篇文章来细细说它。性能

2、加载一个 Typeface

Typeface 的细节,要讲内容仍是挺多的,切听我细细道来。字体

2.1 经过 AssetManager 加载字体

通常咱们会将须要的内置字体文件,放在 assets 目录下面,以后就能够经过 Typeface.createFromAsset() 方法,得到一个 Typeface 对象。线程

例如,如今在项目的 assets/fonts 目录下,放一个字体 .ttf 文件。3d

/f-path.png
/f-path.png

而后,咱们就能够在须要的时候加载它,这也是一段比较常见的代码。code

/f-createfont.png
/f-createfont.png

继续看看 createFromAsset() 的源码。

/f-createassets.png
/f-createassets.png

代码很简单,逻辑也很清晰。

首先会有判断 sFallbackFonts 不能为 null ,不然直接抛出异常,sFallbackFonts 不是重点,这个以后再讲。

它依赖 sDynamicTypefaceCache 来保证线程的安全。而且会使用 createAssetUid() 来获取到这个字体的惟一 key ,经过这个惟一 key ,从 sDynamicTypefaceCache 中获取已经被加载过的字体,若是没有的话,再建立一个 FontFamily 的对象,经过 FontFamily.addFontFromAsset() 方法,将这个字体文件加入进去,最后经过 createFromFamiliesWithDefault() 中,直接建立一个字体,最终存放到 sDynamicTypefaceCache 中去作一道缓存。

createFromFamiliesWithDefault() 方法须要传递一个 FontFamily 的数组,它自己也只是将这些 FontFamily 所表明的共性提取出来,最终调用 nativeCreateFromArray() 这个 native 的方法,因此效率上应该不会有太大的问题。

这也说明,其实放在 assets 目录下的字体,只要经过 Typeface 加载过以后,它自己就会有一道缓存,以后再取也只是从缓存中获取,并不会影响性能。

而 sDynamicTypefaceCache 是一个基于 Lru 算法的,最大存储 16 个字体的一个缓存。

/f-sdyna.png
/f-sdyna.png

2.2 经过文件路径加载字体

Typeface 除了能够从 assets 目录下,加载字体文件,它还能够加载其它地方存储的字体文件,并提供了方便的 Api。

/f-createfromfile.png
/f-createfromfile.png

最终也是经过字体文件的绝对路径进行加载,这部分逻辑也很好理解。同样是使用到了 FontFamily ,同样是使用到了 createFromFamilyWithDefault()

这些并无用到什么新的内容,就再也不展开细说一遍了。

2.3 经过字体名称获取字体

咱们知道,Typeface 还能够管理一些 Android 系统自带的字体,这些字体,若是想要获取,也能够经过 Typeface 来加载,只须要传递进去对应的名称便可。

/f-createname.png
/f-createname.png

能够看到,它除了须要传递一个 familyName 以外,还须要传递一个 style ,这里的 style ,就是以前说的 android:textStyle 传递的值,用于设定字体的粗体(bold)、斜体(italic)等参数的。

这个方法,其实最终调用的是另一个 create() 方法的重载,这个方法后面会详细讲解到。将它单拎出来说解,是由于它其中涉及到一个 sSystemFontMap 对象。

sSystemFontMap 是在 Typeface 的初始化方法 init() 中进行初始化的。

/f-init.jpg
/f-init.jpg

能够看到,它其实是经过 getSystemFontConfigLocation() 中,读取到本地支持的字体文件,而后将它们一次性加载进行,供后面直接使用。

/f-getSystemFont.png
/f-getSystemFont.png

秉承了 Linux 的传统,全部的配置都写在文件里,这里也是直接从文件里读取,getSystemFontConfigLocation() 方法获取到的只是一个配置的路径,最终读取的是 FONTS_CONFIG 配置的 fonts.xml 文件。

2.4 经过 Typeface 得到一个新的 Typeface

到这里,该讲到前面提到的 create() 方法了,这里须要传递进来一个 Typeface 对象,并经过设置 style,为这个原始的 Typeface 字体类附加新的效果。

/f-createtypeface.png
/f-createtypeface.png

而这个过程也是不须要咱们额外关心效率的问题的。它也提供了一个 sTypefaceCache 的缓存,来缓存咱们曾经使用的的系统默认字体。

3、Typeface 的其它细节

到这里基本上就已经讲解清楚 Typeface 的使用了,可是还有一些其它的细节,能够单独拎出来进行额外的讲解。

3.1 Typeface 的初始化

Typeface 的初始化,是放在静态代码块中的,它会初始化一些咱们经常使用的系统默认字体,存储起来方便咱们使用。

/f-static.png
/f-static.png

这里会先调用 init() 方法,加载系统自带的字体,而后再初始化一系列,例如 DEFAULT 、SNAS_SERIF 等自带字体。

因此若是咱们只是须要获取一个系统自带的字体,直接使用这里初始化的一些常量字体便可。

它还会将 DEFAULT 字体,默认初始化一个 sDefaults 的数组,在其中帮咱们预加载好粗体、斜体等经常使用的 Style。

若是想要使用它,Typeface 也提供了对应的方法。

/f-defaultStyle.png
/f-defaultStyle.png

3.2 Typeface 中的 Style

前面一直有提到一个 Style 的概念,它是能够经过 android:textStyle 属性设置的,包括粗体、斜体等样式。

在 Typeface 中,这些样式也对应了一个个的常量,而且 Typeface 也提供了对应的 Api,让咱们获取到当前字体的样式。

/f-fontStyle.png
/f-fontStyle.png

3.3 Typeface 中的 Native 方法

在 Typeface 中,全部最终操做到加载字体的部分,所有都是 native 的方法。而 native 方法就是以效率著称的,这里只须要保证不频繁的调用(Typeface 已经作好了缓存,不会频繁的调用),基本上也不会存在效率的问题。

/f-native.png
/f-native.png

3.4 简单了解一下 FontFamily

FontFamily 在前面不少方法内都用到了。它实际上就是去读取字体文件的数据流,而后再经过 native 方法去加载字体。

/f-addfont.png
/f-addfont.png

addFont() 方法举例,它会先获取 FileInputStream 对象,转换成一个 ByteBuffer 而后传递给 native 方法 nAddFont() 来加载字体。

这个对象,了解一下就能够了,没有什么太复杂的逻辑。

4、小结

到这里就已经讲解清楚 Typeface 的全部内容,看完本篇文章内心也有底去使用 Typeface 了。

总结来讲:

  1. Typeface 提供了一系列的 createXxx() 方法用于从不一样的地方加载字体。
  2. Typeface 支持从系统默认字体、字体文件以及 assets 目录下,加载字体。
  3. Typeface 自己已经支持字体缓存,咱们只须要放心使用,不须要自身再额外缓存一遍。
  4. Typeface 内部最终调用的都是 native 方法,因此也不存在什么效率的问题。

下篇预告

下期会介绍一些比较粗暴的替换全局字体的方案。有在新项目上的,也有在现有的成熟项目上的。期待你的持续关注。

另外,最近有一个关于跳槽的分享,我这边独家有一些优惠活动。若是你有兴趣,能够去看看《看我如何拿到上亿用户 App 家的 offer》。

公众号二维码.jpg
公众号二维码.jpg

点赞或者分享吧~

相关文章
相关标签/搜索