在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对象,从性能角度上说,这个办法更优。

Logo

华为开发者空间,是为全球开发者打造的专属开发空间,汇聚了华为优质开发资源及工具,致力于让每一位开发者拥有一台云主机,基于华为根生态开发、创新。

更多推荐