前言
安卓4.0之后系统设置->显示里面新增了字体大小设置的选项.看下你自己的安卓机,如果你将字体大小从小号一直增大到特大号,有没有发现某些安装的app里面的字体大小也随着变化了?这样可能是便于了阅读,但是app里面本来能显示完全的内容可能因此就只显示了部分.实际上,我们可以看一下QQ、淘宝等大厂的应用,里面的字体大小并不会随着系统设置的大小而变化.
初始解决方法
网上早已给出了解决办法,在Application,也就是app定义的入口处添加以下代码:
@Override
public void onCreate(){
super.onCreate();
Resources resources = getResources();
resources.getConfiguration().fontScale = 1.0f;
resources.updateConfiguration(null, null);
}
就是说,在应用初始化的时候,通过上下文得到应用的资源,进而得到对应于当前应用资源的配置(Configuration),将配置里代表字体缩放的变量(fontScale)置为1,也就是默认值,不进行任何缩放.
新的问题
这时候如果先设置好系统字体的大小为超大或其它,再次进入应用,字体仍然保持之前的大小.但是!如果你没有对该页面(设定最简单的情况,是一个Activity)进行任何设置,那么切换屏幕方向,比如从竖屏到横屏,会发现字体大小又变化了,本来可以显示完全的现在换行了.日志打印获取其字体大小也可以证明确实是变化了:
I/MainActivity_LOG: 该页面重新创建
I/MainActivity_LOG: 字体的大小:48.0
I/MainActivity_LOG: onStart
I/MainActivity_LOG: onResume
I/MainActivity_LOG: onPause
I/MainActivity_LOG: onStop
I/MainActivity_LOG: onDestroy
I/MainActivity_LOG: 该页面重新创建
I/MainActivity_LOG: 字体的大小:62.0
意识到可能跟屏幕方向切换后,页面销毁后重新创建的过程有关.而屏幕方向是设备配置的一个属性,屏幕旋转又是影响配置变化的因素之一.所以要看一下Configuration Change.
Configuration变化后页面的生命周期
public class MainActivity extends AppCompatActivity {
public static final String TAG = "MainActivity_LOG";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.i(TAG, "该页面重新创建");
TextView textView = findViewById(R.id.textView);
float textSize = textView.getTextSize();
Log.i(TAG, "字体的大小:" + textSize);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
Log.i(TAG, "config更新了");
}
@Override
protected void onStart() {
super.onStart();
Log.i(TAG, "onStart");
}
@Override
protected void onResume() {
super.onResume();
Log.i(TAG, "onResume");
}
@Override
protected void onPause() {
super.onPause();
Log.i(TAG, "onPause");
}
@Override
protected void onStop() {
super.onStop();
Log.i(TAG, "onStop");
}
@Override
protected void onDestroy() {
super.onDestroy();
Log.i(TAG, "onDestroy");
}
}
查看日志后,发现:
1.清单文件中不声明任何属性,切屏后Activity销毁后重建,重新走了一遍生命周期,并且不会回调onConfiguration方法,切横屏和切竖屏都是.日志如下:
I/MainActivity_LOG: onPause
I/MainActivity_LOG: onStop
I/MainActivity_LOG: onDestroy
I/MainActivity_LOG: 该页面重新创建
I/MainActivity_LOG: onStart
I/MainActivity_LOG: onResume
2.在清单文件中声明Activity的android:configChanges
属性,如果只是单独声明orientation或者screenSize,和没有声明的情况一样.
3,在清单文件中声明android:configChanges="orientation|screenSize"
,才可以成功回调onConfigurationChanged方法.并且此时不会销毁当前的Activity,也就是不会重走各个生命周期.
原因分析
到这大概可以清楚,虽然应用初始化的时候更新了Configure的fontScale=1,但如果出现了销毁页面重新创建的情况,之前保存的配置就会失效.其实稍微看一下源代码就明白了,调用getResources().getConfiguration()
返回的是在ResourcesImpl类中初始化的时候创建的Configuration对象,调用其无参构造方法后,最终调用的是unSet方法:
public void unset() {
setToDefaults();
fontScale = 0;
}
该方法最终将字体缩放值置为0,也就是适应系统字体的变化,就出现了前面说的问题.
Configuration Changes引起的其它情况
一开始,我采取的是配置清单文件中Activity的属性,比如将屏幕的方向写死android:screenOrientation="portrait"
,或者避免Activity销毁重建android:configChanges="orientation|screenSize"
,这样做的话,旋转屏幕不会造成已修改的配置失效.但是引起Configuration Changes的情况很多,不仅仅是切屏:比如修改设备的默认语言,修改系统字体大小.从打印日志可以看出这时候页面仍然进行了销毁和重建的过程,已更新的字体大小配置又失效了.
解决方法
现在提供了一种解决方法,虽然不太优雅.既然外部的各种操作无法全部考虑到,并且都可能会引发页面的销毁和重建,也就是说无法控制Configuration的重置,在不能修改API的情况下,能想到的就是在基类BaseActivity(或其它)初始化的时候都调用一下更新字体配置的方法.或者更好一点的方式,Application提供了一个监听方法registerActivityLifecycleCallbacks,可以全局监听应用里面所有已声明的Activity的生命周期.当监听到应用创建的时候,都更新一下.
public class MainApplication extends Application {
@Override
public void onCreate() {
super.onCreate();
this.registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(Activity activity, Bundle savedInstanceState) {
Resources resources = getResources();
resources.getConfiguration().fontScale = 1.0f;
resources.updateConfiguration(null, null);
}
@Override
public void onActivityStarted(Activity activity) {
}
@Override
public void onActivityResumed(Activity activity) {
}
@Override
public void onActivityPaused(Activity activity) {
}
@Override
public void onActivityStopped(Activity activity) {
}
@Override
public void onActivitySaveInstanceState(Activity activity, Bundle outState) {
}
@Override
public void onActivityDestroyed(Activity activity) {
}
});
}
}
总结
关于字体大小还有一种解决方式,就是在布局文件中全部用px表示,因为手机的分辨率就是以px定义的,所以无论系统怎么设置字体大小,配置如何更新,显示在界面上的字体大小都是固定的.当然这也带来了适配的问题,因为谷歌官方建议用dp定义尺寸的.这涉及到了安卓的屏幕兼容性问题,可以用鸿洋的方法解决:Android屏幕适配方案.不过这种方法自己根据屏幕分辨率计算出的以160dpi为基准的px大小,没有考虑到屏幕尺寸,也是无法解决所有的问题.
目前我测试没有发现明显的问题,当然方式有点简单粗暴.如果有更好的实现方式,也希望各位能告诉我一下,欢迎交流.