App應用字體大小保持固定以及關於Configuration的變化

前言

安卓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大小,沒有考慮到屏幕尺寸,也是無法解決所有的問題.
目前我測試沒有發現明顯的問題,當然方式有點簡單粗暴.如果有更好的實現方式,也希望各位能告訴我一下,歡迎交流.

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章