1. 程式人生 > >Android系統字型大小如何影響app的字型大小?

Android系統字型大小如何影響app的字型大小?

在Android應用開發過程中,一定會碰到本來完美的佈局,在系統字型大小設定【最大】時變成一團漿糊。解決辦法網上也有很多,但是分析原理的卻幾乎沒看到。博主在碰到問題的第一時間也是直接用了網上的方法,即在BaseActivity中重寫getResources方法如下

@Override  
public Resources getResources() {  
    Resources res = super.getResources();    
    Configuration config=new Configuration();    
    config.setToDefaults();    
    res.updateConfiguration(config,res.getDisplayMetrics());  
    return
res; }

這個方法是有效的。
但是作為開發人員必須懂得舉一反三,而要舉一反三就必須要“知其所以然”,於是博主就去探尋了一番,才有了此篇文章。

1.字型大小如何設定

這個簡單,就拿TextView來說,設定字型大小呼叫setTextSize方法就行了。如果你直接以px為單位設定字型大小,那麼應用字型大小是絕對不會被系統字型所影響的,但是絕大多數時候,我們用的單位都是sp,sp是什麼?sp = scaled pixel 即縮放了的畫素值。而如果用了sp就一定會受系統字型大小的影響。
我們拿TextView來舉例,看看它的setTextSize方法

public void setTextSize
(int unit, float size) { Context c = getContext(); Resources r; if (c == null) r = Resources.getSystem(); else r = c.getResources(); setRawTextSize(TypedValue.applyDimension( unit, size, r.getDisplayMetrics())); }

看一眼程式碼,至少可以確定一件事情:設定的字型大小在內部做了計算和變換。
沒錯,就是這個applyDimension方法乾的好事!!
我們跟進去看看先:

    public static float applyDimension(int unit, float value,
                                       DisplayMetrics metrics)
    {
        switch (unit) {
        case COMPLEX_UNIT_PX:
            return value;
        case COMPLEX_UNIT_DIP:
            return value * metrics.density;
        case COMPLEX_UNIT_SP://看這裡,看這裡
            return value * metrics.scaledDensity;
        case COMPLEX_UNIT_PT:
            return value * metrics.xdpi * (1.0f/72);
        case COMPLEX_UNIT_IN:
            return value * metrics.xdpi;
        case COMPLEX_UNIT_MM:
            return value * metrics.xdpi * (1.0f/25.4f);
        }
        return 0;
    }

事實一目瞭然,我們設定進去的value值,這個方法返回的是value*metrics.scaledDensity 問題肯定出在scaledDensity這個屬性身上!!!
好了,字型大小的設定到此結束。

2. scaledDensity真面目

根據網上提供的辦法,我們知道字型大小的影響跟Resources有關(具體原因待分析)。於是博主在Resources.java原始碼中進行了查詢,發現了一個重要的方法

public void updateConfiguration(Configuration config,
            DisplayMetrics metrics) {
        updateConfiguration(config, metrics, null);
    }

然後是具體實現的方法:

 public void updateConfiguration(Configuration config,
            DisplayMetrics metrics, CompatibilityInfo compat) {
        synchronized (mAccessLock) {
            ... 省略程式碼 ...

            mMetrics.scaledDensity = mMetrics.density * mConfiguration.fontScale;

            ... 省略程式碼 ...
        }
        synchronized (sSync) {
            if (mPluralRule != null) {
                mPluralRule = NativePluralRules.forLocale(config.locale);
            }
        }
    }

相信大家都看到了,mMetrics.scaledDensity的值其實是受mConfiguration.fontScale影響的。那麼我們的研究物件又一次轉變了,變成了fontScale
此時,我們對照一下,網上的解決系統字型大小影響的程式碼片段

@Override  
public Resources getResources() {  
    Resources res = super.getResources();    
    Configuration config=new Configuration();    
    config.setToDefaults();    
    res.updateConfiguration(config,res.getDisplayMetrics());  
    return res;  
}  

看來路子是走對了,這裡也是對Configuration進行了設定,並且還呼叫了updateConfiguration方法。
並且我還告訴你,這裡的config.setToDefaults()方法內部第一句程式碼就是fontScale=1。
那麼分析到這裡,我們基本可以推測:如果修改系統的字型大小,fontScale肯定會改變!

3. 最終解決

首先,推測是需要驗證的。我們可以編寫一個demo,在MainActivity的onCreate方法中列印fontScale值

 Log.d("Javine","fontScale = "+getResources().getConfiguration().fontScale);

博主的是錘子T1,打印出來的結果是
標準 —– fontScale = 1.0
較大 —– fontScale = 1.1
最大 —– fontScale = 1.4
到此為止,我們還是不能百分之百確定問題一定是因為fontScale值的變化引起的。
我們還需要做最後一件事情——通過修改fontScale值來修復字型大小的問題!
跟網上的解決辦法一樣,過載Activity的getResources方法如下:

 @Override
    public Resources getResources() {
        //獲取到resources物件
        Resources res = super.getResources();
        //修改configuration的fontScale屬性
        res.getConfiguration().fontScale = 1;
        //將修改後的值更新到metrics.scaledDensity屬性上
        res.updateConfiguration(null,null);
        return res;
    }

經博主測試,問題完美解決!!並且,對比網上的解決辦法,我們並沒有新建一個Configuration物件,從效能角度上說,這個辦法更優。