Android 另一種更換字體的方式

碎碎唸

不知道什么時候,自己項目中的TextView會自己變成AppCompatTextView。因為項目時間趕。不是特別的重視這個問題。再后來遇到了一個需求,那就是需要全局更改項目中的數(shù)字字體。這個時候我又重新開始思考AppCompatTextView的問題。

因為在之前的項目中更換過字體。只需自己繼承自TextView,然后在構(gòu)造方法中設(shè)置一下字體。就可以完成自定義字體了。

public class FontTextView extends AppCompatTextView {


    public FontTextView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        setTypeface(TypeFaceHelper.getInstance().getFineTypeFace());
    }

}

因為之前的項目是在項目早期,就已經(jīng)決定使用自定義字體。所以我們當(dāng)初規(guī)定所有的文字控件都是以FontTextView
但是,現(xiàn)在我們的項目進(jìn)行著迭代了很久,如果繼續(xù)使用這樣方式工作量會很大,而且容易出錯。所以我們需要一種比較簡單的方式,來完成這件事。在開工之前,我希望能夠先處理以下兩個問題。

  1. 為什么TextView會被替換成AppCompatTextView呢?
  2. 能不能將AppCompatTextView替換成我們的FontTextView呢?

查找

我開始嘗試的去尋找在什么時候,我們的TextView被替換了。我們可以在AppCompatTextView的構(gòu)造方法中下個斷點。這樣當(dāng)AppCompatTextView 被創(chuàng)建的話,我們就可以知道什么時候它被創(chuàng)建了。 下面是它的函數(shù)調(diào)用棧。

image.png

從上面的函數(shù)調(diào)用棧中,在我們xml解析完成之后,然后會調(diào)用createView這個方法來創(chuàng)建view。我們重點查看一下createView這個方法

switch (name) {
            case "TextView":
                view = createTextView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageView":
                view = createImageView(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Button":
                view = createButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "EditText":
                view = createEditText(context, attrs);
                verifyNotNull(view, name);
                break;
            case "Spinner":
                view = createSpinner(context, attrs);
                verifyNotNull(view, name);
                break;
            case "ImageButton":
                view = createImageButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "CheckBox":
                view = createCheckBox(context, attrs);
                verifyNotNull(view, name);
                break;
            case "RadioButton":
                view = createRadioButton(context, attrs);
                verifyNotNull(view, name);
                break;
            case "CheckedTextView":

這里會根據(jù)xml解析出來的標(biāo)簽,從而創(chuàng)建對應(yīng)的控件。具體查看一下TextView在是什么時候被創(chuàng)建.

 @NonNull
    protected AppCompatTextView createTextView(Context context, AttributeSet attrs) {
        return new AppCompatTextView(context, attrs);
    }

這樣我們就已經(jīng)知道了,我們的TextViewxml解析完成之后會被替換成AppCompatTextView了。那么現(xiàn)在如果能夠?qū)?code>AppCompatTextView替換成我們的FontTextView就能完成自定義字體了。

替換

通過上面的函數(shù)棧,可以發(fā)現(xiàn)我onCreateViewAppCompatDelegateImpl里面的一個方法。

image.png

那么我們能不能重寫這個方法,然后將返回值改成我們特定的FontTextView呢?

答案是一定的,但是這里有一個需要注意的地方。因為AppCompatDelegateImpl是包訪問權(quán)限,正常情況下你是沒有訪問權(quán)限的。但是你可以把你的包名改成和它一致,這樣你就具有訪問權(quán)限了。


//包名一致
package androidx.appcompat.app;



public class FontTextDelegate extends AppCompatDelegateImpl {

    public FontTextDelegate(Context context, Window window, AppCompatCallback callback) {
        super(context, window, callback);
    }


    @Override
    public View createView(View parent, String name, @NonNull Context context, @NonNull AttributeSet attrs) {
        if (name.equals("TextView")) {
            return new FontTextView(context, attrs);
        }
        return super.createView(parent, name, context, attrs);
    }

}


我們已經(jīng)重寫了AppCompatDelegateImpl的方法了,現(xiàn)在只需要查看AppCompatDelegateImpl在什么時候被初始換。然后替換成我們重寫后的子類即可完成目標(biāo)了。還是一樣的方式,在AppCompatDelegateImpl構(gòu)造方法中設(shè)一個斷點。

image.png

通過函數(shù)調(diào)用??梢钥吹?,AppCompatDelegateImpl是在AppCompatActivity中的getDelegate初始換的,那么我們只需要重寫我們項目中的ActivitygetDelegate方法。就可以替換成AppCompatDelegateImpl了。

以上!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容