天天看点

全局修改默认字体,通过反射也能做到

本文讲的是<b>全局修改默认字体,通过反射也能做到,</b>

本文作者:张旸在 Android 下使用自定义字体已经是一个比较常见的需求了,最近也做了个比较深入的研究。

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

期待你持续关注。

本篇是本系列的第四篇,之前已经发布的文章,有兴趣可以先看看。

<a href="http://zhuanlan.51cto.com/art/201709/551993.htm">Android 字体修改概述|开篇</a>

<a href="http://zhuanlan.51cto.com/art/201709/552322.htm">修改字体需要了解 Typeface 的所有细节</a>

<a href="http://zhuanlan.51cto.com/art/201709/552504.htm">简单粗暴的方式,修改字体</a>

一、前言

本文依然属于 Android 修改字体的系列,本系列开始会介绍一些比较方便的全局修改的方案,越往后的方案可能会越好一些,但是不一定最适用你现在的项目。

今天介绍的就是其中的一个,使用反射的方式,修改 Typeface 中的某个字体,来达到全局替换的目的。

二、替换默认字体的思路

本文的很多预备的知识点,应该在之前的文章中就已经说清楚了,有兴趣可以去看看完整的文章,《想修改 Android 字体,你需要先了解一下 Typeface!!!》。

这里为了保证逻辑完整,还是大概说一下思路。

2.1 修改Typeface 的某个默认字体

首先需要明确一点,在 Android 中,所有操作字体的动作,都会使用到 Typeface 这个类。而系统默认的一些字体,也会在 Typeface 被加载的时候进行初始化,因为这些步骤在它的静态代码块内完成。

全局修改默认字体,通过反射也能做到

而这些字体都会定义成了 static final 的,所以一般我们是不能去修改它们的。

全局修改默认字体,通过反射也能做到

但是我们是可以使用反射的方式去修改被标记为 static final 的常量的,这个后面再将细节。

也就是说,我们只需要在初始化的实际,替换掉某个默认的字体,然后在Theme 内将默认字体字体标记为该字体,就可以达到替换的目的。

2.2 在 TextView 中默认的字体

在 TextView 的构造方法里,设置字体的方法是 setTypefaceFromAttrs() ,下面是该方法的方法签名。

全局修改默认字体,通过反射也能做到

在该方法的参数中,如果 familyName 为 null 的时候,会根据传入的 typefaceIndex 去设置对应的字体,传入到 setTypeface() 方法中。

再来看看 TextView 的构造方法中,获取这几个参数的地方。在默认什么都不设置的情况下, familyName 就是为 null,而 typefaceIndex 为 -1。这两个参数会先从 TextAppearance 中读取属性,再从 TextView 本身设置的 xml 属性中读取,后者会覆盖前者。

可是 typefaceIndex 还会有一些其它的操作,例如 inputType 为 password 的时候,就会强行修改为 MONOSPACE。

全局修改默认字体,通过反射也能做到

最终,将处理后的结构,传递给 setTypefaceFromAttrs() 方法。

通过这些细节,我们就可以了解到,是在有一些情况下,是可以保证 TextView 使用的是我们的某个被加载到 Typeface 中的默认字体的。

条件就是:

fontFamily == null &amp;&amp; typefaceIndex != -1 

2.3 在 Theme 中,修改字体为默认字体样式

对于一些默认的字体样式,是可以直接在 Theme 中进行设置的,它的优先级低于在页面布局的 xml 中,为 TextView 设置的字体样式,但是如果不设置,那么在 Theme 中的设置将会生效。

这个没什么好说的,我这里用的主题就是 AppTheme,所以我在它里面修改 android:typeface 就可以了。

全局修改默认字体,通过反射也能做到

三、通过反射修改字体

到这里,基本的概念就已经讲解清楚了,那么我们就开始实际编写代码来替换字体了。

3.1 修改 Theme

在 App 的主题中,修改 android:typeface 为 serif。

全局修改默认字体,通过反射也能做到

注意,这里随便选了一个默认字体,实际上使用 monospace 也是可以的,只需要和后面我们替换的字体保持一致即可。

当然这里不推荐使用 monospace ,因为 TextView 本身还有一些逻辑会将 typefaceIndex 设置成 monospace,所以我们不要替换它比较好。

3.2 通过反射修改 Typeface 的字体

在 Typeface 中,是有一些被标记为 static final 的默认字体,因为上一步的 Theme 中,就是设置的 serif ,所以我们这里替换它就好了。

完整的方法非常的简单,就是通过反射拿到 Typeface.SERIF,然后使用反射将它修改成我们需要的字体即可。

全局修改默认字体,通过反射也能做到

因为这里修改了 static final 的值,所以需要额外调用 setAccessible() 方法,它会修改 AccessibleObject 中的 overide 为 true,这个标记的意思,就是关闭对这个字段改写的安全检查,从而让我们可以替换 static final 的字段。

3.3 在入口的地方,调用替换的方法

接下来就清晰了,我们只需要在 App 启动的时候,调用一下 changeDefaultFont() 方法。

这里直接在 Application.onCreate() 方法中,调用即可。

全局修改默认字体,通过反射也能做到

3.4 验证运行结果

这个没什么了,直接写个 Demo,正常使用 TextView 就可以了。

全局修改默认字体,通过反射也能做到

来源:51CTO

原文标题:全局修改默认字体,通过反射也能做到

继续阅读