全局替换字体,开源库更方便!!!
序
在 Android 下使用自定义字体已经是一个比较常见的需求了,最近也做了个比较深入的研究。
那么按照惯例我又要出个一篇有关 Android 修改字体相关的文章,但是写下来发现内容还挺多的,所以我决定将它们拆分一下,分几篇来详细的讲解。主要会是一些常用的替换字体的方案,最后还会介绍一些全局替换的方案,当然也会包含最新的 『Fonts in XML』的方案。
期待你持续关注。
本篇是本系列的第九篇,之前已经发布的文章,有兴趣可以先看看。
- Android 字体修改概述|开篇
- 修改字体需要了解 Typeface 的所有细节
- 简单粗暴的方式,修改字体
- 利用反射,修改全局字体
- 利用 AppCompatDelegate ,全局替换全局字体
- 通过修改 LayoutInflater,全局替换字体!
- 自定义属性,支持多个字体文件!
- Android 可下载字体,Font in xml!
一、前言
之前已经介绍了很多种,快速、低入侵的替换全局字体的方式。但是大多数情况下,我们需要实现的功能,一定已经有现成的实现方案。
本文就介绍一个 Github 上,比较火的全局替换字体的开源库,差不多阅读文档加集成,一个小时全局替换字体不是梦。
这个开源替换字体库就是 Calligraphy:
二、如何使用Calligraphy
既然是要接入开源库来全局替换字体,先来看看它可以实现的效果。
/screenshot.png接下来,我们开始一步步集成它。
2.1 添加 Gradle 依赖
Calligraphy 支持 Gradle 和 jar 的接入方式,这里使用 Gradle 来接入。
/gradle-denpen.png2.2 添加字体文档到项目内
Calligraphy 支持的文件,可以放在 assets/
目录下,当然,我们可以再在其中建立一个文件夹来专门的存放字体文件。
2.3 初始化 Calligraphy
Calligraphy 使用 CalligraphyConfig 类,来进行初始化。它需要在 App 的入口,Application.onCreate()
中调用。
初始化主要是为了指定一些默认的配置,例如:默认字体、默认属性值。
2.4 替换 Context
Calligraphy 对 Activity 的 Context,进行了一次包装,需要使用它包装的 Context,才可以达到替换字体的效果。所以还需要重写 BaseActivity 中的 attachBaseContext()
方法,将其替换成 Calligraphy 为我们提供的 Context 的包装类 CalligraphyContextWrapper。
2.5 使用 Calligraphy
到这里,就完成了 Calligraphy 的配置了,我们只需要在 TextView 中,通过属性去使用它就好了,它配置的是我们字体文件,在 assets 目录下的路径。
/text-layout-xml.png2.6 查缺补漏
Calligraphy 使用起来还是很方便的,并且也支持更多的配置方式,例如: Style、Theme 都可以。
具体的使用细节,大家还是阅读文档了解更方便。
三、Calligraphy的原理
我们使用一个开源库,当然要理解它的原理才能放心使用在商业项目上,接下来,我们就来分析一下 Calligraphy 的实现原理,看看和之前介绍的方式,有没有什么区别。
先来看看 Calligraphy 的整体结构。
/call-path.png可以看到,它一共需要的类非常的少,算是一个比较精简的库了,并且它并没有重写 TextView ,所以应该是通过其它的方式来做到字体的替换的。
我们先来看看在 Application 需要调用的配置类, CalligraphyConfig 的源码。
/config-methon.pngCalligraphyConfig 使用 Builder 的模式去初始化自己,可以看到这里只是设置了一些配置项,并没有实际的业务逻辑。
/config-get.pngCalligraphyConfig 初始化之后,就以静态变量存储起来,供其它地方使用,是一种单例的模式,但是并没有考虑线程安全的问题。
既然 CalligraphyConfig 没有实际的逻辑,那么接下去应该如何追踪重要的代码呢?
仔细观察之前配置项里,需要重写 Activity.attachBaseContext()
方法,这里会传递它重写的一个 Context 的包装类 CalligraphyContextWrapper,所以接下来我们再看看 CalligraphyContextWrapper 的源码逻辑。
读了 CalligraphyContextWrapper 源码之后,你会发现它最重要的就是重写了 getSystemService()
方法,当它是 LAYOUT_INFLATER_SERVICE 的时候,将自己的 CalligraphyLayoutInflater 类,返回回去。
那么,这里的 LAYOUT_INFLATER_SERVICE 到底是什么呢?
我想大家应该对 LayoutInflater 不陌生,从 layout-xml 加载 View 的时候,都需要用到它,相信下面这段代码,应该大家都不陌生。
/layoutinfalter-code.png再仔细看看 LayoutInflater.from()
方法的源码。
可以看到,这里获得 LayoutInflater 对象的时候,用到的就是 LAYOUT_INFLATER_SERVICE。
所以 CalligraphyContextWrapper.getSystemService() 方法被重写的目的,就是为了替换掉 LayoutInflater 对象,所以可以猜想,设置自定义字体的地方,就在自定义的 LayoutInflater 中。
继续查看 CalligraphyLayoutInflater 的源码,最终修改字体的逻辑,是在 CalligraphyContextWrappe 的 onViewCreatedInternal()
方法里面。
它会取出我们自定义属性上设置的值,然后设置到初始化好的 TextView 上去。
四、Calligraphy 小结
到此就完成了 Calligraphy 的主要逻辑追踪,几个核心技术点:
- Calligraphy 不需要重写 TextView 之类的控件。
- Calligraphy 重写了 LayoutInflater 。
- Calligraphy 在
attachBaseContext()
方法中,替换掉 ContextWrapper。 - 又通过自定义的 ContextWrapper 的
getSystemService()
方法,将 LayoutInflater 替换成库里重写的 CalligraphyLayoutInflater。 - 在 CalligraphyLayoutInflater 中,拦截我们需要的 TextView 和其子类,对它们的字体替换成我们设置的字体。
当然,实际上,开源库之所以可以流传的比较广,它还做了更多的细节处理,但是我们一般分析开源库,只需要关心主线逻辑就可以了。
整体来说 Calligraphy 没有什么大毛病,可以放心使用,当然如果你用了一些同样依赖此原理的第三方库,可能会有冲突,这个就只能具体问题具体分析了。
今天在承香墨影公众号的后台,回复『成长』。我会送你一些我整理的学习资料,包含:Android反编译、算法。Web项目源码。
推荐阅读:
点赞或者分享吧~