全局修改默認(rèn)字體,通過(guò)反射也能做到
序
在 Android 下使用自定義字體已經(jīng)是一個(gè)比較常見(jiàn)的需求了,最近也做了個(gè)比較深入的研究。
那么按照慣例我又要出個(gè)一篇有關(guān) Android 修改字體相關(guān)的文章,但是寫下來(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é)
一、前言
本文依然屬于 Android 修改字體的系列,本系列開(kāi)始會(huì)介紹一些比較方便的全局修改的方案,越往后的方案可能會(huì)越好一些,但是不一定最適用你現(xiàn)在的項(xiàng)目。
今天介紹的就是其中的一個(gè),使用反射的方式,修改 Typeface 中的某個(gè)字體,來(lái)達(dá)到全局替換的目的。
二、替換默認(rèn)字體的思路
本文的很多預(yù)備的知識(shí)點(diǎn),應(yīng)該在之前的文章中就已經(jīng)說(shuō)清楚了,有興趣可以去看看完整的文章,《想修改 Android 字體,你需要先了解一下 Typeface!!!》。
這里為了保證邏輯完整,還是大概說(shuō)一下思路。
2.1 修改Typeface 的某個(gè)默認(rèn)字體
首先需要明確一點(diǎn),在 Android 中,所有操作字體的動(dòng)作,都會(huì)使用到 Typeface 這個(gè)類。而系統(tǒng)默認(rèn)的一些字體,也會(huì)在 Typeface 被加載的時(shí)候進(jìn)行初始化,因?yàn)檫@些步驟在它的靜態(tài)代碼塊內(nèi)完成。
而這些字體都會(huì)定義成了 static final 的,所以一般我們是不能去修改它們的。
但是我們是可以使用反射的方式去修改被標(biāo)記為 static final 的常量的,這個(gè)后面再將細(xì)節(jié)。
也就是說(shuō),我們只需要在初始化的實(shí)際,替換掉某個(gè)默認(rèn)的字體,然后在Theme 內(nèi)將默認(rèn)字體字體標(biāo)記為該字體,就可以達(dá)到替換的目的。
2.2 在 TextView 中默認(rèn)的字體
在 TextView 的構(gòu)造方法里,設(shè)置字體的方法是 setTypefaceFromAttrs() ,下面是該方法的方法簽名。
在該方法的參數(shù)中,如果 familyName 為 null 的時(shí)候,會(huì)根據(jù)傳入的 typefaceIndex 去設(shè)置對(duì)應(yīng)的字體,傳入到 setTypeface() 方法中。
再來(lái)看看 TextView 的構(gòu)造方法中,獲取這幾個(gè)參數(shù)的地方。在默認(rèn)什么都不設(shè)置的情況下, familyName 就是為 null,而 typefaceIndex 為 -1。這兩個(gè)參數(shù)會(huì)先從 TextAppearance 中讀取屬性,再?gòu)?TextView 本身設(shè)置的 xml 屬性中讀取,后者會(huì)覆蓋前者。
可是 typefaceIndex 還會(huì)有一些其它的操作,例如 inputType 為 password 的時(shí)候,就會(huì)強(qiáng)行修改為 MONOSPACE。
最終,將處理后的結(jié)構(gòu),傳遞給 setTypefaceFromAttrs() 方法。
通過(guò)這些細(xì)節(jié),我們就可以了解到,是在有一些情況下,是可以保證 TextView 使用的是我們的某個(gè)被加載到 Typeface 中的默認(rèn)字體的。
條件就是:
- fontFamily == null && typefaceIndex != -1
2.3 在 Theme 中,修改字體為默認(rèn)字體樣式
對(duì)于一些默認(rèn)的字體樣式,是可以直接在 Theme 中進(jìn)行設(shè)置的,它的優(yōu)先級(jí)低于在頁(yè)面布局的 xml 中,為 TextView 設(shè)置的字體樣式,但是如果不設(shè)置,那么在 Theme 中的設(shè)置將會(huì)生效。
這個(gè)沒(méi)什么好說(shuō)的,我這里用的主題就是 AppTheme,所以我在它里面修改 android:typeface 就可以了。
三、通過(guò)反射修改字體
到這里,基本的概念就已經(jīng)講解清楚了,那么我們就開(kāi)始實(shí)際編寫代碼來(lái)替換字體了。
3.1 修改 Theme
在 App 的主題中,修改 android:typeface 為 serif。
注意,這里隨便選了一個(gè)默認(rèn)字體,實(shí)際上使用 monospace 也是可以的,只需要和后面我們替換的字體保持一致即可。
當(dāng)然這里不推薦使用 monospace ,因?yàn)?TextView 本身還有一些邏輯會(huì)將 typefaceIndex 設(shè)置成 monospace,所以我們不要替換它比較好。
3.2 通過(guò)反射修改 Typeface 的字體
在 Typeface 中,是有一些被標(biāo)記為 static final 的默認(rèn)字體,因?yàn)樯弦徊降?Theme 中,就是設(shè)置的 serif ,所以我們這里替換它就好了。
完整的方法非常的簡(jiǎn)單,就是通過(guò)反射拿到 Typeface.SERIF,然后使用反射將它修改成我們需要的字體即可。
因?yàn)檫@里修改了 static final 的值,所以需要額外調(diào)用 setAccessible() 方法,它會(huì)修改 AccessibleObject 中的 overide 為 true,這個(gè)標(biāo)記的意思,就是關(guān)閉對(duì)這個(gè)字段改寫的安全檢查,從而讓我們可以替換 static final 的字段。
3.3 在入口的地方,調(diào)用替換的方法
接下來(lái)就清晰了,我們只需要在 App 啟動(dòng)的時(shí)候,調(diào)用一下 changeDefaultFont() 方法。
這里直接在 Application.onCreate() 方法中,調(diào)用即可。
3.4 驗(yàn)證運(yùn)行結(jié)果
這個(gè)沒(méi)什么了,直接寫個(gè) Demo,正常使用 TextView 就可以了。

【本文為51CTO專欄作者“張旸”的原創(chuàng)稿件,轉(zhuǎn)載請(qǐng)通過(guò)微信公眾號(hào)聯(lián)系作者獲取授權(quán)】