需求一:设置APP字体大小不随系统字体大小而改变
需求二:判断是否需要进入适老版
为什么要设置APP字体大小不随系统字体大小而改变
这是因为如果APP中字体大小随系统设置的字体大小而改变,UI上就要做出大量的适配工作。从UI设计到代码实现,越是内容丰富的界面越难实现,甚至需要专门再写一套适老版UI。在字体调大以后,APP部分界面可能已经在面目全非,而且部分手机是适老版手机,字体默认状态下就比正常手机要大一些,APP安装在这种手机上,UI会出现一定问题。所以要么从UI上适配字体可调整大小,可以根据字体大小展示适老版界面,或是像微信、短信等APP完全适配各种字体大小;要么设置APP字体大小不随系统字体大小而改变,代表性APP就是“个人所得税”APP,该APP的用户群体是全国所有纳税人员,除了小学生,什么人都用得上,该APP字体大小不会随任何系统设置而改变,而且字体也是自定义的,不随系统的字体样式而改变。
调大字体后UI出现问题示例图如下,下图可以看到,字体变得很大,部分图标和文字重叠了。
影响字体大小因素:
- 1.系统设置修改字体大小;
- 2.系统设置修改显示大小。 备注:简易模式(老年人模式)是将显示大小调大,图标、字体大小都会变大。部分机型专门针对老人用户,默认状态就是简易模式。所以如果APP用户群体包含老年人,则需要考虑适配简易模式。
设置界面如下:
设置字体大小和显示大小
简易模式
控制sp dp换算关系的源码如下:
1dp = metrics.density * 1px
1sp = metrics.scaledDensity * 1px
无论我们给textSize设置dp、sp还是其它单位,最终都会转换成px.
TypedValue.java
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;
}
设置APP字体大小不随系统字体大小而改变
方案一:设置字体大小单位将sp替换成dp
dp换算成px不受系统设置修改字体大小而改变。但实际开发中这样并不可行,因为APP会集成很多第三方SDK,这些SDK的UI一般会使用sp设置字体大小,SDK的布局替换会很麻烦,有些字体大小是通过代码动态设置的,不可能修改。所以这个方案并不可行。
备注:textView.setTextSize(textSize) API默认的单位是SP,而不是DP。
方案二:使用config.setToDefault() API恢复默认设置
Configuration configuration = getResources().getConfiguration();
configuration.setToDefaults();
源码中是将fontScale恢复默认值1。
public void setToDefaults() {
fontScale = 1;
。。。。
}
在Activity的super.onCreate()方法前调用如上代码。setToDefaults()只能将fontScale=1,这是将修改字体大小恢复默认状态。但是如果修改显示大小,那么字体大小还是会随显示大小而改变。 config.setToDefault() API,将系统设置的字体大小恢复至默认状态,也不能完全保证字体大小不会发生改变。当系统设置修改显示大小或设置“简易模式”时,字体大小还是会发生改变。因为在这种状态下,修改的是resource的density,此时density scaledDensity 的值都发生了改变。示例:1080*1920的手机,DPI是480,density=3.0,1dp=3px,当调大显示大小后,density可能是3.+,以华为P30手机为例,简易模式下density=3.375,默认density=3.此时1dp=3.375px。所以在这种情况下,无论字体大小设置的是dp、sp,还是使用config.setToDefault(),都没有用,字体大小还是会改变的。\
metrics.scaledDensity会受config.fontScale 和 metrics.density的值影响。
示例:华为P30手机,默认状态DPI=480,density=3.0,fontScale=1;1dp=3px。修改字体大小至最大,修改显示大小为最大,此时config.fontScale=1.45, metrics.density=3.375, metrics.scaledDensity = 4.89375.
结论:config.setToDefault() 方法并不能完全保证字体不随系统设置而改变。
方案三:计算系统默认的density值,修改DisplayMetrics对象的density、scaledDensity为默认的density值。
在Activity的super.onCreate方法前添加如下代码,将字体、dp的缩放系数都设置为默认值。
备注:
- 实际开发会在MyApplication中监听Activity的生命周期,在onCreate方法回调中执行如下代码。
- 部分SDK的UI为了适配字体大小,可能使用了config.setToDefault() API,这样我们无论怎么设置都不会生效,而这种适配方案有一定局限性。config.setToDefault()与displayMetrics.density = defaultDensity是相冲突的,使用了前者,后者设置会失效。针对这种情况只能束手无策,电信一键登录SDK似乎就是这样的。
- 每个context有自己的Resource对象,在实际开发中,除了要设置Activity的Resource对象的displayMetrics.density等之外,还需要将Application的Resource对象进行处理。例如一些第三方工具类,经常会使用Application对象获取Resource,然后进行dp与px转换,如果不处理,最终换算的值则依然会受系统设置影响。
int densityDefault = DisplayMetrics.DENSITY_DEFAULT;//值为160
int densityDeviceStable = DisplayMetrics.DENSITY_DEVICE_STABLE;//默认的DPI
int defaultDensity = densityDeviceStable / densityDefault;//计算默认的density
Resources resources = getResources();
DisplayMetrics displayMetrics = resources.getDisplayMetrics();
displayMetrics.density = defaultDensity;//dp换算px
displayMetrics.scaledDensity = defaultDensity;//sp换算px
displayMetrics.densityDpi = densityDeviceStable;
Configuration configuration = getResources().getConfiguration();
configuration.fontScale = 1;//字体缩放系数恢复默认
判断是否需要进入适老版。
直接判断 displayMetrics.scaledDensity 值与默认值的大小关系即可。一般先判断字体大小是否调大,然后在进入首页后弹窗提示用户:是否进入适老版
默认的displayMetrics.scaledDensity = displayMetrics.density,相当于1sp = 1dp的关系。
displayMetrics.scaledDensity > defaultDensity