天天看點

Android中使用反射方法建立Typeface執行個體

背景

在大多手機上可以通過

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,導緻當建立的字庫中缺少某字時,無法使用系統提供的回落字庫繪制,顯示為空白。