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