在 Android 下使用自定義字體已是一個比較常見的需求了,最近也作了個比較深刻的研究。android
那麼按照慣例我又要出個一篇有關 Android 修改字體相關的文章,可是寫下來發現內容還挺多的,因此我決定將它們拆分一下,分幾篇來詳細的講解(多是五篇)。主要會是一些經常使用的替換字體的方案,最後還會介紹一些全局替換的方案,固然也會包含最新的 『Fonts in XML』的方案。算法
期待你持續關注。數組
本篇是本系列的第二篇,以前已經發布的文章,有興趣能夠先看看。緩存
若是你想要操做字體,不管是使用 Android 系統自帶的字體,仍是加載本身內置的 .ttf(TureType) 或者 .otf(OpenType) 格式的字體文件,你都須要使用到 Typeface 這個類。安全
本文就單獨來分析 Typeface 的一些源碼細節,本文在本系列中,可能相對枯燥一些,可是我以爲它又是不可或缺的一部分,因此單獨拿出一篇文章來細細說它。性能
Typeface 的細節,要講內容仍是挺多的,切聽我細細道來。字體
通常咱們會將須要的內置字體文件,放在 assets 目錄下面,以後就能夠經過 Typeface.createFromAsset()
方法,得到一個 Typeface 對象。線程
例如,如今在項目的 assets/fonts 目錄下,放一個字體 .ttf 文件。3d
而後,咱們就能夠在須要的時候加載它,這也是一段比較常見的代碼。code
繼續看看 createFromAsset()
的源碼。
代碼很簡單,邏輯也很清晰。
首先會有判斷 sFallbackFonts 不能爲 null ,不然直接拋出異常,sFallbackFonts 不是重點,這個以後再講。
它依賴 sDynamicTypefaceCache 來保證線程的安全。而且會使用 createAssetUid()
來獲取到這個字體的惟一 key ,經過這個惟一 key ,從 sDynamicTypefaceCache 中獲取已經被加載過的字體,若是沒有的話,再建立一個 FontFamily 的對象,經過 FontFamily.addFontFromAsset()
方法,將這個字體文件加入進去,最後經過 createFromFamiliesWithDefault()
中,直接建立一個字體,最終存放到 sDynamicTypefaceCache 中去作一道緩存。
createFromFamiliesWithDefault()
方法須要傳遞一個 FontFamily 的數組,它自己也只是將這些 FontFamily 所表明的共性提取出來,最終調用 nativeCreateFromArray()
這個 native 的方法,因此效率上應該不會有太大的問題。
這也說明,其實放在 assets 目錄下的字體,只要經過 Typeface 加載過以後,它自己就會有一道緩存,以後再取也只是從緩存中獲取,並不會影響性能。
而 sDynamicTypefaceCache 是一個基於 Lru 算法的,最大存儲 16 個字體的一個緩存。
Typeface 除了能夠從 assets 目錄下,加載字體文件,它還能夠加載其它地方存儲的字體文件,並提供了方便的 Api。
最終也是經過字體文件的絕對路徑進行加載,這部分邏輯也很好理解。同樣是使用到了 FontFamily ,同樣是使用到了 createFromFamilyWithDefault()
。
這些並無用到什麼新的內容,就再也不展開細說一遍了。
咱們知道,Typeface 還能夠管理一些 Android 系統自帶的字體,這些字體,若是想要獲取,也能夠經過 Typeface 來加載,只須要傳遞進去對應的名稱便可。
能夠看到,它除了須要傳遞一個 familyName 以外,還須要傳遞一個 style ,這裏的 style ,就是以前說的 android:textStyle
傳遞的值,用於設定字體的粗體(bold)、斜體(italic)等參數的。
這個方法,其實最終調用的是另一個 create()
方法的重載,這個方法後面會詳細講解到。將它單拎出來說解,是由於它其中涉及到一個 sSystemFontMap 對象。
sSystemFontMap 是在 Typeface 的初始化方法 init()
中進行初始化的。
能夠看到,它其實是經過 getSystemFontConfigLocation(
) 中,讀取到本地支持的字體文件,而後將它們一次性加載進行,供後面直接使用。
秉承了 Linux 的傳統,全部的配置都寫在文件裏,這裏也是直接從文件裏讀取,getSystemFontConfigLocation()
方法獲取到的只是一個配置的路徑,最終讀取的是 FONTS_CONFIG
配置的 fonts.xml 文件。
到這裏,該講到前面提到的 create()
方法了,這裏須要傳遞進來一個 Typeface 對象,並經過設置 style,爲這個原始的 Typeface 字體類附加新的效果。
而這個過程也是不須要咱們額外關心效率的問題的。它也提供了一個 sTypefaceCache 的緩存,來緩存咱們曾經使用的的系統默認字體。
到這裏基本上就已經講解清楚 Typeface 的使用了,可是還有一些其它的細節,能夠單獨拎出來進行額外的講解。
Typeface 的初始化,是放在靜態代碼塊中的,它會初始化一些咱們經常使用的系統默認字體,存儲起來方便咱們使用。
這裏會先調用 init()
方法,加載系統自帶的字體,而後再初始化一系列,例如 DEFAULT 、SNAS_SERIF 等自帶字體。
因此若是咱們只是須要獲取一個系統自帶的字體,直接使用這裏初始化的一些常量字體便可。
它還會將 DEFAULT 字體,默認初始化一個 sDefaults 的數組,在其中幫咱們預加載好粗體、斜體等經常使用的 Style。
若是想要使用它,Typeface 也提供了對應的方法。
前面一直有提到一個 Style 的概念,它是能夠經過 android:textStyle
屬性設置的,包括粗體、斜體等樣式。
在 Typeface 中,這些樣式也對應了一個個的常量,而且 Typeface 也提供了對應的 Api,讓咱們獲取到當前字體的樣式。
在 Typeface 中,全部最終操做到加載字體的部分,所有都是 native 的方法。而 native 方法就是以效率著稱的,這裏只須要保證不頻繁的調用(Typeface 已經作好了緩存,不會頻繁的調用),基本上也不會存在效率的問題。
FontFamily 在前面不少方法內都用到了。它實際上就是去讀取字體文件的數據流,而後再經過 native 方法去加載字體。
拿 addFont()
方法舉例,它會先獲取 FileInputStream 對象,轉換成一個 ByteBuffer 而後傳遞給 native 方法 nAddFont()
來加載字體。
這個對象,瞭解一下就能夠了,沒有什麼太複雜的邏輯。
到這裏就已經講解清楚 Typeface 的全部內容,看完本篇文章內心也有底去使用 Typeface 了。
總結來講:
createXxx()
方法用於從不一樣的地方加載字體。下期會介紹一些比較粗暴的替換全局字體的方案。有在新項目上的,也有在現有的成熟項目上的。期待你的持續關注。
另外,最近有一個關於跳槽的分享,我這邊獨家有一些優惠活動。若是你有興趣,能夠去看看《看我如何拿到上億用戶 App 家的 offer》。
點贊或者分享吧~