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物件,從效能角度上說,這個辦法更優。