为什么设置系统字体,应用中字体会根据系统字体改变
设置系统字体显示大小后,应用中的字体也根据也跟随系统设置而变化,这样是不是之前写好的整个布局就有可能乱了。
拿TextView源码看下面setTextSize源码看下设置字体细节
/**
* unit 设置字体的单位 比如 sp,px,dp等等
* size 字体大小
**/
public void setTextSize(int unit, float size) {
if (!isAutoSizeEnabled()) {
setTextSizeInternal(unit, size, true /* shouldRequestLayout */);
}
}
继续往下看
private void setTextSizeInternal(int unit, float size, boolean shouldRequestLayout) {
Context c = getContext();
Resources r;
// 获取Resources 对象,当前View没有就获取系统的一般当前view都会有
if (c == null) {
r = Resources.getSystem();
} else {
r = c.getResources();
}
// 将设置的字体大小根据 unit 单位和DisplayMetrics进行换算
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()),
shouldRequestLayout);
}
继续看下TypedValue.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;
}
可以看出,根据不同unit计算出来的字体大小不一样,px是不需要就算,,dip是根据density 去计算,sp是根据 scaledDensity计算字体大小,pt是根据xdpi 和一个指定指数计算等等。当改变系统现实大小的时候,参与字体计算的值只要有变化都会改变字体大小。
通过改变系统字体大小,打log看以DisplayMetrics中的值只有scaledDensity 这个值有变化 (可以参考) 所以只要我们在系统字体改变的时候,控制这个值不要改变就可以了,那这个值是在那里赋值?通过一顿瞎点,点到了ResourceImpl这个类里边的updateConfiguration
方法,并且在方法中看到了这个字段的赋值
public void updateConfiguration(Configuration config, DisplayMetrics metrics,
CompatibilityInfo compat) {
//.....
// Protect against an unset fontScale.
mMetrics.scaledDensity = mMetrics.density *
(mConfiguration.fontScale != 0 ? mConfiguration.fontScale : 1.0f);
// .....
}
原来scaledDensity 是fontScale和density计算得来的,density 改变系统字体是不会变化的,那就是fontScale这玩意导致的,那这玩意是那的,通过一顿点击发现是Configuration类的一个public 字段,那这fontScale这个字段系统是什么时候设置的呢?一顿点和排查最后找到了ActivityManageService中的updateFontScaleIfNeeded的方法
private void updateFontScaleIfNeeded(@UserIdInt int userId) {
//.....
synchronized (this) {
if (getGlobalConfiguration().fontScale == scaleFactor) {
return;
}
//.....
}
再看下这个方法是哪里调用的,又是一通乱点发现是在ActivityManageService中的一个内部类FontScaleSettingObserver
中的一个onChange方法中调用的,
private final class FontScaleSettingObserver extends ContentObserver {
@Override
public void onChange(boolean selfChange, Uri uri, @UserIdInt int userId) {
if (mFontScaleUri.equals(uri)) {
updateFontScaleIfNeeded(userId);
} else if (mHideErrorDialogsUri.equals(uri)) {
synchronized (ActivityManagerService.this) {
updateShouldShowDialogsLocked(getGlobalConfiguration());
}
}
}
}
我猜这个肯定是一个观察者模式,观察到系统配置有相关变化,随后去设置一系列的值,那我们也可以手动的改变这个fontScale值为基准的,不让其随着系统改变而改变、
网上找了段代码,在activity中添加就可以了,源码也不知道分析有误没,如果有误欢迎来提
@Override
public Resources getResources() {
Resources resources = super.getResources();
Configuration configuration =new Configuration();
configuration.setToDefaults();
resources.updateConfiguration(configuration,resources.getDisplayMetrics());
return resources;
}