背景
在大多手機上可以通過
android.graphics.Typeface#createFromFile(java.lang.String)
方法建立一個Typeface執行個體;但有些ROM需要通過反射方法實作建立Typeface執行個體。
1. 通用方法
Typeface typeface = Typeface.createFromFile(path); // 建立Typeface執行個體
textView.setTypeface(typeface); // 設定到TextView
複制
2. 反射方法
對于通用方法失效的情況,通過反射方法調用Typeface及FontFamily相關方法,進而繞過ROM修改的設定字型方法,建立Typeface;而FontFamily類方法在不同Android版本的實作有一定的不同,是以反射方案也有多種。對于API≤20時,可按通用方法處理。經過對非公開SDK的嘗試,逐漸發現了以下的反射方案。
1) Android 8.x (API 26, 27)
依次反射調用以下方法
android.graphics.Typeface#init()
android.graphics.FontFamily#addFont(String path, int ttcIndex, FontVariationAxis[] axes, int weight, int italic)
android.graphics.FontFamily#allowUnsupportedFont()
android.graphics.FontFamily#freeze()
android.graphics.Typeface#nativeCreateFromArray(long[] familyArray, int weight, int italic)
複制
2) Android 7.x (API 24, 25)
android.graphics.Typeface#init()
android.graphics.FontFamily#addFont(String path, int ttcIndex)
android.graphics.Typeface#nativeCreateFromArray(FontFamily[] families)
複制
3) Android 5.x 6.x (API 21, 22, 23)
android.graphics.Typeface#init()
android.graphics.FontFamily#addFont(String path)
android.graphics.Typeface#nativeCreateFromArray(FontFamily[] families)
複制
此方案與7.x方案相比,調用方法名相同,但addFont的參數略有不同。但實作後發現在部分ROM中仍失效。經過分析,在華為ROM5.x~6.x中,對Typeface#init的方法也進行了屏蔽,是以實作了另一種方案。
android.graphics.FontFamily#nCreateFamily(String lang, int variant) // 建立FontFamily執行個體,取到mNativePtr
android.graphics.FontFamily#nAddFont(long nativeFamily, String path) // 建立Font
android.graphics.Typeface#nativeCreateFromArray(long[] familyArray) // 建立Typeface
android.graphics.Typeface#Typeface(long ni) // 構造Typeface執行個體
複制
3. 最終方案
後續又經過嘗試發現,在7.x~8.x的廠商ROM中,隻調用
Typeface#init()
方法,重新初始化之後即可直接調用
Typeface#createFromFile()
方法建立Typeface執行個體;但5.x~6.x仍需使用原方案。總結方案如下:
1) Android 7.x~8.x (API 24~27)
android.graphics.Typeface#init()
Typeface typeface = Typeface.createFromFile(path); // 建立Typeface執行個體
複制
2) Android 5.x~6.x (API 21~23)
android.graphics.Typeface#init()
android.graphics.FontFamily#addFont(String path)
android.graphics.Typeface#nativeCreateFromArray(FontFamily[] families)
複制
3) Android 4.x (API 15~20)及通用方法
Typeface typeface = Typeface.createFromFile(path); // 建立Typeface執行個體
複制
4. Android P
在Android P(API 28)中,Google開始收攏對非公開API的使用,前述反射方法在Google公布的淺灰名單中,在Android P中仍可調用,但存在不能調用的風險,可考慮向Google申請加白名單。
5.其他
另外,使用反射方案建立的Typeface缺少sFallbackFonts,導緻當建立的字庫中缺少某字時,無法使用系統提供的回落字庫繪制,顯示為空白。