想修改 Android 字體,你需要先了解一下 Typeface?。?!
序
在 Android 下使用自定義字體已經(jīng)是一個(gè)比較常見的需求了,最近也做了個(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』的方案。
本篇是本系列的第二篇,之前已經(jīng)發(fā)布的文章,有興趣可以先看看。
一、開篇
如果你想要操作字體,無(wú)論是使用 Android 系統(tǒng)自帶的字體,還是加載自己內(nèi)置的 .ttf(TureType) 或者 .otf(OpenType) 格式的字體文件,你都需要使用到 Typeface 這個(gè)類。
本文就單獨(dú)來(lái)分析 Typeface 的一些源碼細(xì)節(jié),本文在本系列中,可能相對(duì)枯燥一些,但是我覺得它又是不可或缺的一部分,所以單獨(dú)拿出一篇文章來(lái)細(xì)細(xì)說(shuō)它。
二、加載一個(gè) Typeface
Typeface 的細(xì)節(jié),要講內(nèi)容還是挺多的,切聽我細(xì)細(xì)道來(lái)。
2.1 通過(guò) AssetManager 加載字體
一般我們會(huì)將需要的內(nèi)置字體文件,放在 assets 目錄下面,之后就可以通過(guò) Typeface.createFromAsset() 方法,獲得一個(gè) Typeface 對(duì)象。
例如,現(xiàn)在在項(xiàng)目的 assets/fonts 目錄下,放一個(gè)字體 .ttf 文件。
然后,我們就可以在需要的時(shí)候加載它,這也是一段比較常見的代碼。
繼續(xù)看看 createFromAsset() 的源碼。
代碼很簡(jiǎn)單,邏輯也很清晰。
首先會(huì)有判斷 sFallbackFonts 不能為 null ,否則直接拋出異常,sFallbackFonts 不是重點(diǎn),這個(gè)之后再講。
它依賴 sDynamicTypefaceCache 來(lái)保證線程的安全。并且會(huì)使用 createAssetUid() 來(lái)獲取到這個(gè)字體的唯一 key ,通過(guò)這個(gè)唯一 key ,從 sDynamicTypefaceCache 中獲取已經(jīng)被加載過(guò)的字體,如果沒有的話,再創(chuàng)建一個(gè) FontFamily 的對(duì)象,通過(guò) FontFamily.addFontFromAsset() 方法,將這個(gè)字體文件加入進(jìn)去,***通過(guò) createFromFamiliesWithDefault() 中,直接創(chuàng)建一個(gè)字體,最終存放到 sDynamicTypefaceCache 中去做一道緩存。
createFromFamiliesWithDefault() 方法需要傳遞一個(gè) FontFamily 的數(shù)組,它本身也只是將這些 FontFamily 所代表的共性提取出來(lái),最終調(diào)用 nativeCreateFromArray() 這個(gè) native 的方法,所以效率上應(yīng)該不會(huì)有太大的問(wèn)題。
這也說(shuō)明,其實(shí)放在 assets 目錄下的字體,只要通過(guò) Typeface 加載過(guò)之后,它本身就會(huì)有一道緩存,之后再取也只是從緩存中獲取,并不會(huì)影響性能。
而 sDynamicTypefaceCache 是一個(gè)基于 Lru 算法的,***存儲(chǔ) 16 個(gè)字體的一個(gè)緩存。
2.2 通過(guò)文件路徑加載字體
Typeface 除了可以從 assets 目錄下,加載字體文件,它還可以加載其它地方存儲(chǔ)的字體文件,并提供了方便的 Api。
最終也是通過(guò)字體文件的絕對(duì)路徑進(jìn)行加載,這部分邏輯也很好理解。一樣是使用到了 FontFamily ,一樣是使用到了 createFromFamilyWithDefault()。
這些并沒有用到什么新的內(nèi)容,就不再展開細(xì)說(shuō)一遍了。
2.3 通過(guò)字體名稱獲取字體
我們知道,Typeface 還可以管理一些 Android 系統(tǒng)自帶的字體,這些字體,如果想要獲取,也可以通過(guò) Typeface 來(lái)加載,只需要傳遞進(jìn)去對(duì)應(yīng)的名稱即可。
可以看到,它除了需要傳遞一個(gè) familyName 之外,還需要傳遞一個(gè) style ,這里的 style ,就是之前說(shuō)的 android:textStyle 傳遞的值,用于設(shè)定字體的粗體(bold)、斜體(italic)等參數(shù)的。
這個(gè)方法,其實(shí)最終調(diào)用的是另外一個(gè) create() 方法的重載,這個(gè)方法后面會(huì)詳細(xì)講解到。將它單拎出來(lái)講解,是因?yàn)樗渲猩婕暗揭粋€(gè) sSystemFontMap 對(duì)象。
sSystemFontMap 是在 Typeface 的初始化方法 init() 中進(jìn)行初始化的。
可以看到,它實(shí)際上是通過(guò) getSystemFontConfigLocation() 中,讀取到本地支持的字體文件,然后將它們一次性加載進(jìn)行,供后面直接使用。
秉承了 Linux 的傳統(tǒng),所有的配置都寫在文件里,這里也是直接從文件里讀取,getSystemFontConfigLocation() 方法獲取到的只是一個(gè)配置的路徑,最終讀取的是 FONTS_CONFIG 配置的 fonts.xml 文件。
2.4 通過(guò) Typeface 獲得一個(gè)新的 Typeface
到這里,該講到前面提到的 create() 方法了,這里需要傳遞進(jìn)來(lái)一個(gè) Typeface 對(duì)象,并通過(guò)設(shè)置 style,為這個(gè)原始的 Typeface 字體類附加新的效果。
而這個(gè)過(guò)程也是不需要我們額外關(guān)心效率的問(wèn)題的。它也提供了一個(gè) sTypefaceCache 的緩存,來(lái)緩存我們?cè)?jīng)使用的的系統(tǒng)默認(rèn)字體。
三、Typeface 的其它細(xì)節(jié)
到這里基本上就已經(jīng)講解清楚 Typeface 的使用了,但是還有一些其它的細(xì)節(jié),可以單獨(dú)拎出來(lái)進(jìn)行額外的講解。
3.1 Typeface 的初始化
Typeface 的初始化,是放在靜態(tài)代碼塊中的,它會(huì)初始化一些我們常用的系統(tǒng)默認(rèn)字體,存儲(chǔ)起來(lái)方便我們使用。
這里會(huì)先調(diào)用 init() 方法,加載系統(tǒng)自帶的字體,然后再初始化一系列,例如 DEFAULT 、SNAS_SERIF 等自帶字體。
所以如果我們只是需要獲取一個(gè)系統(tǒng)自帶的字體,直接使用這里初始化的一些常量字體即可。
它還會(huì)將 DEFAULT 字體,默認(rèn)初始化一個(gè) sDefaults 的數(shù)組,在其中幫我們預(yù)加載好粗體、斜體等常用的 Style。
如果想要使用它,Typeface 也提供了對(duì)應(yīng)的方法。
3.2 Typeface 中的 Style
前面一直有提到一個(gè) Style 的概念,它是可以通過(guò) android:textStyle 屬性設(shè)置的,包括粗體、斜體等樣式。
在 Typeface 中,這些樣式也對(duì)應(yīng)了一個(gè)個(gè)的常量,并且 Typeface 也提供了對(duì)應(yīng)的 Api,讓我們獲取到當(dāng)前字體的樣式。
3.3 Typeface 中的 Native 方法
在 Typeface 中,所有最終操作到加載字體的部分,全部都是 native 的方法。而 native 方法就是以效率著稱的,這里只需要保證不頻繁的調(diào)用(Typeface 已經(jīng)做好了緩存,不會(huì)頻繁的調(diào)用),基本上也不會(huì)存在效率的問(wèn)題。
3.4 簡(jiǎn)單了解一下 FontFamily
FontFamily 在前面很多方法內(nèi)都用到了。它實(shí)際上就是去讀取字體文件的數(shù)據(jù)流,然后再通過(guò) native 方法去加載字體。
拿 addFont() 方法舉例,它會(huì)先獲取 FileInputStream 對(duì)象,轉(zhuǎn)換成一個(gè) ByteBuffer 然后傳遞給 native 方法 nAddFont() 來(lái)加載字體。
這個(gè)對(duì)象,了解一下就可以了,沒有什么太復(fù)雜的邏輯。
四、小結(jié)
到這里就已經(jīng)講解清楚 Typeface 的所有內(nèi)容,看完本篇文章心里也有底去使用 Typeface 了。
總結(jié)來(lái)說(shuō):
Typeface 提供了一系列的 createXxx()方法,用于從不同的地方加載字體。
Typeface 支持從系統(tǒng)默認(rèn)字體、字體文件以及 assets 目錄下,加載字體。
Typeface 本身已經(jīng)支持字體緩存,我們只需要放心使用,不需要自身再額外緩存一遍。
Typeface 內(nèi)部最終調(diào)用的都是 native 方法,所以也不存在什么效率的問(wèn)題。

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












































