全局替換字體,開源庫更方便!!!

在 Android 下使用自定義字體已是一個比較常見的需求了,最近也作了個比較深刻的研究。git

那麼按照慣例我又要出個一篇有關 Android 修改字體相關的文章,可是寫下來發現內容還挺多的,因此我決定將它們拆分一下,分幾篇來詳細的講解。主要會是一些經常使用的替換字體的方案,最後還會介紹一些全局替換的方案,固然也會包含最新的 『Fonts in XML』的方案。github

期待你持續關注。算法

本篇是本系列的第九篇,以前已經發布的文章,有興趣能夠先看看。安全

1、前言

以前已經介紹了不少種,快速、低入侵的替換全局字體的方式。可是大多數狀況下,咱們須要實現的功能,必定已經有現成的實現方案。app

本文就介紹一個 Github 上,比較火的全局替換字體的開源庫,差很少閱讀文檔加集成,一個小時全局替換字體不是夢。ide

這個開源替換字體庫就是 Calligraphy:學習

https://github.com/chrisjenx/...字體

2、如何使用Calligraphy

既然是要接入開源庫來全局替換字體,先來看看它能夠實現的效果。gradle

/screenshot.png

接下來,咱們開始一步步集成它。ui

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 均可以。

具體的使用細節,你們仍是閱讀文檔瞭解更方便。

3、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 上去。

4、Calligraphy 小結

到此就完成了 Calligraphy 的主要邏輯追蹤,幾個核心技術點:

  1. Calligraphy 不須要重寫 TextView 之類的控件。
  2. Calligraphy 重寫了 LayoutInflater 。
  3. Calligraphy 在 attachBaseContext() 方法中,替換掉 ContextWrapper。
  4. 又經過自定義的 ContextWrapper 的 getSystemService() 方法,將 LayoutInflater 替換成庫裏重寫的 CalligraphyLayoutInflater。
  5. 在 CalligraphyLayoutInflater 中,攔截咱們須要的 TextView 和其子類,對它們的字體替換成咱們設置的字體。

固然,實際上,開源庫之因此能夠流傳的比較廣,它還作了更多的細節處理,可是咱們通常分析開源庫,只須要關心主線邏輯就能夠了。

總體來講 Calligraphy 沒有什麼大毛病,能夠放心使用,固然若是你用了一些一樣依賴此原理的第三方庫,可能會有衝突,這個就只能具體問題具體分析了。

今天在承香墨影公衆號的後臺,回覆『成長』。我會送你一些我整理的學習資料,包含:Android反編譯、算法。Web項目源碼。

推薦閱讀:

點贊或者分享吧~

相關文章
相關標籤/搜索