在 Android 下使用自定义字体已是一个比较常见的需求了,最近也作了个比较深刻的研究。android
那么按照惯例我又要出个一篇有关 Android 修改字体相关的文章,可是写下来发现内容还挺多的,因此我决定将它们拆分一下,分几篇来详细的讲解(多是五篇)。主要会是一些经常使用的替换字体的方案,最后还会介绍一些全局替换的方案,固然也会包含最新的 『Fonts in XML』的方案。安全
期待你持续关注。app
本篇是本系列的第四篇,以前已经发布的文章,有兴趣能够先看看。ide
本文依然属于 Android 修改字体的系列,本系列开始会介绍一些比较方便的全局修改的方案,越日后的方案可能会越好一些,可是不必定最适用你如今的项目。布局
今天介绍的就是其中的一个,使用反射的方式,修改 Typeface 中的某个字体,来达到全局替换的目的。字体
本文的不少预备的知识点,应该在以前的文章中就已经说清楚了,有兴趣能够去看看完整的文章,《》。spa
这里为了保证逻辑完整,仍是大概说一下思路。3d
首先须要明确一点,在 Android 中,全部操做字体的动做,都会使用到 Typeface 这个类。而系统默认的一些字体,也会在 Typeface 被加载的时候进行初始化,由于这些步骤在它的静态代码块内完成。code
而这些字体都会定义成了 static final
的,因此通常咱们是不能去修改它们的。xml
可是咱们是可使用反射的方式去修改被标记为 static final
的常量的,这个后面再将细节。
也就是说,咱们只须要在初始化的实际,替换掉某个默认的字体,而后在Theme 内将默认字体字体标记为该字体,就能够达到替换的目的。
在 TextView 的构造方法里,设置字体的方法是 setTypefaceFromAttrs() ,下面是该方法的方法签名。
在该方法的参数中,若是 familyName 为 null 的时候,会根据传入的 typefaceIndex 去设置对应的字体,传入到 setTypeface()
方法中。
再来看看 TextView 的构造方法中,获取这几个参数的地方。在默认什么都不设置的状况下, familyName 就是为 null,而 typefaceIndex 为 -1。这两个参数会先从 TextAppearance 中读取属性,再从 TextView 自己设置的 xml 属性中读取,后者会覆盖前者。
但是 typefaceIndex 还会有一些其它的操做,例如 inputType 为 password 的时候,就会强行修改成 MONOSPACE。
最终,将处理后的结构,传递给 setTypefaceFromAttrs()
方法。
经过这些细节,咱们就能够了解到,是在有一些状况下,是能够保证 TextView 使用的是咱们的某个被加载到 Typeface 中的默认字体的。
条件就是:
fontFamily == null && typefaceIndex != -1
对于一些默认的字体样式,是能够直接在 Theme 中进行设置的,它的优先级低于在页面布局的 xml 中,为 TextView 设置的字体样式,可是若是不设置,那么在 Theme 中的设置将会生效。
这个没什么好说的,我这里用的主题就是 AppTheme,因此我在它里面修改 android:typeface 就能够了。
到这里,基本的概念就已经讲解清楚了,那么咱们就开始实际编写代码来替换字体了。
在 App 的主题中,修改 android:typeface 为 serif。
注意,这里随便选了一个默认字体,实际上使用 monospace 也是能够的,只须要和后面咱们替换的字体保持一致便可。
固然这里不推荐使用 monospace ,由于 TextView 自己还有一些逻辑会将 typefaceIndex 设置成 monospace,因此咱们不要替换它比较好。
在 Typeface 中,是有一些被标记为 static final 的默认字体,由于上一步的 Theme 中,就是设置的 serif ,因此咱们这里替换它就行了。
完整的方法很是的简单,就是经过反射拿到 Typeface.SERIF,而后使用反射将它修改为咱们须要的字体便可。
由于这里修改了 static final
的值,因此须要额外调用 setAccessible()
方法,它会修改 AccessibleObject 中的 overide 为 true,这个标记的意思,就是关闭对这个字段改写的安全检查,从而让咱们能够替换 static final
的字段。
接下来就清晰了,咱们只须要在 App 启动的时候,调用一下 changeDefaultFont()
方法。
这里直接在 Application.onCreate() 方法中,调用便可。
这个没什么了,直接写个 Demo,正常使用 TextView 就能够了。