利用 AppCompatDelegate ,半小時就能在成熟項目上全局替換字體!
序
在 Android 下使用自定義字體已經是一個比較常見的需求了,最近也做了個比較深入的研究。
那么按照慣例我又要出個一篇有關 Android 修改字體相關的文章,但是寫下來發現內容還挺多的,所以我決定將它們拆分一下,分幾篇來詳細的講解(可能是五篇)。主要會是一些常用的替換字體的方案,***還會介紹一些全局替換的方案,當然也會包含***的 『Fonts in XML』的方案。
期待你持續關注。
本篇是本系列的第五篇,之前已經發布的文章,有興趣可以先看看。
一、前言
之前介紹過不少替換字體的方案,本篇再介紹在一個成熟項目上,無縫替換字體的一種解決方案。
其思路就是利用 Support v7 包中的 AppCompatActivity 的控件代理的思路,通過替換 AppCompatDelegate 對象,來實現將原本的 TextView 替換成我們自定義的,可修改字體的 FontTextView ,從而達到替換字體的目的。
使用本方案的前提是你使用的是 AppCompatActivity。
好了,下面進入主題。
二、Support v7 的代理思路
這里說的 Android Support v7 包內的代理思路,主要是通過 AppCompatDelegate 來實現的,我們先來了解一下它的原理。
AppCompat 在最開始出現在 v7 包中的時候,其實作用非常的小,只是為了讓 Api Level 7+ 的設備,也可以使用 ActionBar 。
而在 Support v7:21 版本之后,AppCompat 承擔了更多的責任,可以為 API Level 7+ 的設備帶來 Material Color Palette 、Widget 著色、ToolBar 等功能。而且之前推薦使用的 ActionBarActivity 也不再推薦使用,取而代之的是 AppCompatActivity。
AppCompatActivity 其實內部的實現原理也和之前的 ActionBarActivity 不同,它是通過 AppCompatDelegate 來實現的。AppCompatActivity 將所有的生命周期相關的回調,都交由 AppCompatDelegate 來處理。
但是實際上它們并沒有直接的關聯關系,我們也可以直接使用 AppCompatDelegate 來放入我們自己實現的 Activity 中,但是這樣就會更麻煩,一般也不推薦這樣使用。
1、AppCompat 因何存在
AppCompat 為了支持 MD 的效果,需要其內部的控件都具有自動著色功能,這樣可以保持 App 在設計上具有一致的體驗和提高認可度。而這些是原本的 UI 控件所不具備的,所以它只好將其需要的 UI 控件全部重寫一遍來支持這個效果。
這些被重寫的 UI 控件,都在 android.support.v7.widget 包下面:
可以看到這些被重寫的 UI 控件都是 AppCompat 開頭的,但是如果重寫了這么多控件,現有項目直接硬替換起來,工作量就會非常大,所以 AppCompatDelegate 這種以代理的方式自動為我們替換所使用的 UI 控件的功能就非常的有必要了。
2、AppCompatDelegate 是如何工作的
在 AppCompatActivity 中,使用 getDelegate() 方法來獲得 AppCompatDelegate 對象的。
而這個 AppCompatDelegate.create() 方法就是其做代理支持的實現。
可以看到它在其中,分別根據不同的 Api Level 進行不同的實現,而且***能支持到 Api Level 9。
雖然這里看到它通過 Api Level 做了區分判斷來做具體的實現,但是類似這種 AppCompatDelegateImplVxx 的類,都高版本的繼承低版本的。而 AppCompatDelegateImplV9 中,就是通過 LayoutInflaterFactory 接口來實現 UI 控件替換的代理。
在解析 ViewTree 的時候,會調用 createView() 方法來得到 View 對象,在其中,又是通過 mAppCompatViewInflater.createView() 來真實的獲取 View 的。
而在 mAppCompatViewInflater.createView() 中,就是通過 UI 控件的名稱,來替換掉我們需要的 AppCompatXxx 的 UI 控件。
所以到這里,就可以發現,如果我們也需要利用 AppCompatDelegate 來替換掉我們需要的 UI 控件,只需要自己實現一個 AppCompatDelegate 以及其中的 callActivityOnCreateView() 方法。在實際使用的 AppCompatActivity 的父類中,重寫 getDelegate() 方法,將方法的返回值替換成我們之前修改過的 AppCompatDelegate ,就可以實現自動替換 UI 控件了。
三、全局替換字體
看到這里,相信已經了解到 AppCompatDelegate 的原理了,也知道我們接下來如何利用它來替換全局的 TextView,從而達到替換字體的目的。
首先,我們需要有一個可修改字體的 FontTextView 控件,它是繼承自 AppCompatTextView 的。
FontTextView 控件的邏輯非常的簡單,就是從 assets 中取出字體,然后再 set 到 TextView 上去。
再在項目內,建立一個 android.support.v7.app 的 Package。在其中,新建一個 FontAppCompatActivity 的類,它是繼承自 AppCompatActivity。
在這里,通過重寫 callActivityOnCreateView() 方法,在識別到是 TextView 之后,將我們重寫的 FontTextView 構造好并返回出去,來達到控件替換的目的。
當然,實際項目中,你還需要 EditTextView、Button 以及你項目內用到的其它用于顯示文字的自定義控件。
需要注意的是,這里的 FontCompatActivity 一定要放在 android.support.v7.app 包下,否者AppCompatDelegateImplV14 你將無法繼承它。
為了方便,我將 FontTextView 也放在此包下了,之中的結構如下:
到這里,就已經完成了基本的替換工作,只需要替換我們 Activity 的父類即可。
***,運行起來看看效果,***的一次替換。
【本文為51CTO專欄作者“張旸”的原創稿件,轉載請通過微信公眾號聯系作者獲取授權】