zoukankan      html  css  js  c++  java
  • 全局替换字体,开源库更方便!!!

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

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

    期待你持续关注。

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

    一、前言

    之前已经介绍了很多种,快速、低入侵的替换全局字体的方式。但是大多数情况下,我们需要实现的功能,一定已经有现成的实现方案。

    本文就介绍一个 Github 上,比较火的全局替换字体的开源库,差不多阅读文档加集成,一个小时全局替换字体不是梦。

    这个开源替换字体库就是 Calligraphy:

    https://github.com/chrisjenx/Calligraphy

    二、如何使用Calligraphy

    既然是要接入开源库来全局替换字体,先来看看它可以实现的效果。

    /screenshot.png

    接下来,我们开始一步步集成它。

    2.1 添加 Gradle 依赖

    Calligraphy 支持 Gradle 和 jar 的接入方式,这里使用 Gradle 来接入。

    /gradle-denpen.png

    2.2 添加字体文档到项目内

    Calligraphy 支持的文件,可以放在 assets/ 目录下,当然,我们可以再在其中建立一个文件夹来专门的存放字体文件。

    /assets-font-file.png

    2.3 初始化 Calligraphy

    Calligraphy 使用 CalligraphyConfig 类,来进行初始化。它需要在 App 的入口,Application.onCreate() 中调用。

    /init-method.png

    初始化主要是为了指定一些默认的配置,例如:默认字体、默认属性值。

    2.4 替换 Context

    Calligraphy 对 Activity 的 Context,进行了一次包装,需要使用它包装的 Context,才可以达到替换字体的效果。所以还需要重写 BaseActivity 中的 attachBaseContext() 方法,将其替换成 Calligraphy 为我们提供的 Context 的包装类 CalligraphyContextWrapper。

    /attach-base-context.png

    2.5 使用 Calligraphy

    到这里,就完成了 Calligraphy 的配置了,我们只需要在 TextView 中,通过属性去使用它就好了,它配置的是我们字体文件,在 assets 目录下的路径。

    /text-layout-xml.png

    2.6 查缺补漏

    Calligraphy 使用起来还是很方便的,并且也支持更多的配置方式,例如: Style、Theme 都可以。

    具体的使用细节,大家还是阅读文档了解更方便。

    三、Calligraphy的原理

    我们使用一个开源库,当然要理解它的原理才能放心使用在商业项目上,接下来,我们就来分析一下 Calligraphy 的实现原理,看看和之前介绍的方式,有没有什么区别。

    先来看看 Calligraphy 的整体结构。

    /call-path.png

    可以看到,它一共需要的类非常的少,算是一个比较精简的库了,并且它并没有重写 TextView ,所以应该是通过其它的方式来做到字体的替换的。

    我们先来看看在 Application 需要调用的配置类, CalligraphyConfig 的源码。

    /config-methon.png

    CalligraphyConfig 使用 Builder 的模式去初始化自己,可以看到这里只是设置了一些配置项,并没有实际的业务逻辑。

    /config-get.png

    CalligraphyConfig 初始化之后,就以静态变量存储起来,供其它地方使用,是一种单例的模式,但是并没有考虑线程安全的问题。

    既然 CalligraphyConfig 没有实际的逻辑,那么接下去应该如何追踪重要的代码呢?

    仔细观察之前配置项里,需要重写 Activity.attachBaseContext() 方法,这里会传递它重写的一个 Context 的包装类 CalligraphyContextWrapper,所以接下来我们再看看 CalligraphyContextWrapper 的源码逻辑。

    /getSystemServer.png

    读了 CalligraphyContextWrapper 源码之后,你会发现它最重要的就是重写了 getSystemService() 方法,当它是 LAYOUT_INFLATER_SERVICE 的时候,将自己的 CalligraphyLayoutInflater 类,返回回去。

    那么,这里的 LAYOUT_INFLATER_SERVICE 到底是什么呢?

    我想大家应该对 LayoutInflater 不陌生,从 layout-xml 加载 View 的时候,都需要用到它,相信下面这段代码,应该大家都不陌生。

    /layoutinfalter-code.png

    再仔细看看 LayoutInflater.from() 方法的源码。

    /layoutinflalter-from.png

    可以看到,这里获得 LayoutInflater 对象的时候,用到的就是 LAYOUT_INFLATER_SERVICE。

    所以 CalligraphyContextWrapper.getSystemService() 方法被重写的目的,就是为了替换掉 LayoutInflater 对象,所以可以猜想,设置自定义字体的地方,就在自定义的 LayoutInflater 中。

    继续查看 CalligraphyLayoutInflater 的源码,最终修改字体的逻辑,是在 CalligraphyContextWrappe 的 onViewCreatedInternal() 方法里面。

    /view-create-interval.png

    它会取出我们自定义属性上设置的值,然后设置到初始化好的 TextView 上去。

    四、Calligraphy 小结

    到此就完成了 Calligraphy 的主要逻辑追踪,几个核心技术点:

    1. Calligraphy 不需要重写 TextView 之类的控件。
    2. Calligraphy 重写了 LayoutInflater 。
    3. Calligraphy 在 attachBaseContext() 方法中,替换掉 ContextWrapper。
    4. 又通过自定义的 ContextWrapper 的 getSystemService() 方法,将 LayoutInflater 替换成库里重写的 CalligraphyLayoutInflater。
    5. 在 CalligraphyLayoutInflater 中,拦截我们需要的 TextView 和其子类,对它们的字体替换成我们设置的字体。

    当然,实际上,开源库之所以可以流传的比较广,它还做了更多的细节处理,但是我们一般分析开源库,只需要关心主线逻辑就可以了。

    整体来说 Calligraphy 没有什么大毛病,可以放心使用,当然如果你用了一些同样依赖此原理的第三方库,可能会有冲突,这个就只能具体问题具体分析了。

    今天在承香墨影公众号的后台,回复『成长』。我会送你一些我整理的学习资料,包含:Android反编译、算法。Web项目源码。

    推荐阅读:

    点赞或者分享吧~

  • 相关阅读:
    Git-更新数据
    iOS开发-基本的网络知识
    iOS开发-单例模式
    iOS开发-多线程知识
    iOS开发-核心动画随笔
    iOS开发-关于网络状态的判断
    毕业设计--天气预报App
    iOS开发-UIColor转UIIamge方法
    iOS开发-用预处理指令代替注释
    JMS 消息服务
  • 原文地址:https://www.cnblogs.com/plokmju/p/7685581.html
Copyright © 2011-2022 走看看