幾需一行代碼完成多語言切換

logo

背景

以前老是有開發者反饋我應用切換了語言,但是工具類獲取的 string 卻沒有發生改變。其實這個問題很簡單,你切換語言的 Context 只做用在了你的 Activity 上,並無對你的 Application 作一樣的操做,知道了這點,那麼解決問題就很簡單了,爲了省事,我給你們封裝了 LanguageUtils,直接一行代碼即可完成多語言的切換,相似微信的語言切換分分鐘即可完成。java

使用

Gradle:android

implementation 'com.blankj:utilcode:latest_version'
複製代碼

APIs

applySystemLanguage     : 設置系統語言
applyLanguage           : 設置語言
isAppliedLanguage       : 是否設置了語言
getAppliedLanguage      : 獲取設置的語言
getContextLanguage      : 獲取上下文的語言
getAppContextLanguage   : 獲取應用上下文的語言
getSystemLanguage       : 獲取系統的語言
updateAppContextLanguage: 更新應用上下文語言
attachBaseContext       : 若是設置語言無效則在 Activity#attachBaseContext 調用它
複製代碼

原理

若是咱們的應用不設置 android:configChanges="locale|layoutDirection",那麼應用是跟隨系統語言設置的變化而變化的,好比你應用適配了英語(values-en-rUS)和簡體中文(values-zh-rCN),那麼你去設置裏切換成英語的話,返回到你應用中,你的 Activity 會從新建立一遍,把 Activity#Resource#Configuration#Locale 設置爲當前系統語言,這樣就達到了跟隨系統語言設置的變化而變化,但 Application 並無重啓,因此這就致使了一開說到的問題。git

要解決跟隨系統變化這一點的話,只須要在 Activity#onCreate 的生命週期中把 Application#Resource#Configuration#Locale 設置爲系統的 Locale 便可,那麼系統的 Locale 怎麼讀取呢,知道以前屏幕適配方案的人應該也能想到這一方式:Resources.getSystem().getConfiguration().locale,這樣,咱們的 Application 便也切換成了系統語言,注意更新 Locale 須要兼容下高版本,調用具體代碼能夠參照以下:github

/** * Update the locale of applicationContext. * * @param destLocale The dest locale. * @param consumer The consumer. */
public static void updateAppContextLanguage(@NonNull Locale destLocale, @Nullable Utils.Consumer<Boolean> consumer) {
    pollCheckAppContextLocal(destLocale, 0, consumer);
}
static void pollCheckAppContextLocal(final Locale destLocale, final int index, final Utils.Consumer<Boolean> consumer) {
    Resources appResources = Utils.getApp().getResources();
    Configuration appConfig = appResources.getConfiguration();
    Locale appLocal = getLocal(appConfig);
    setLocal(appConfig, destLocale);
    // 因爲 updateConfigure 並不會立馬更新(本地測試小於 1ms 便會更新)config,因此須要後面的輪詢來監聽變化來作其餘處理(好比重啓應用)
    Utils.getApp().getResources().updateConfiguration(appConfig, appResources.getDisplayMetrics());
    
    if (consumer == null) return;
    
    if (isSameLocale(appLocal, destLocale)) {
        consumer.accept(true);
    } else {
        if (index < 20) {
            UtilsBridge.runOnUiThreadDelayed(new Runnable() {
                @Override
                public void run() {
                    pollCheckAppContextLocal(destLocale, index + 1, consumer);
                }
            }, 16);
            return;
        }
        Log.e("LanguageUtils", "appLocal didn't update.");
        consumer.accept(false);
    }
}
複製代碼

那麼若是是應用內切換語言呢?咱們能夠仿照系統切換語言的方式,重啓咱們的應用或者重啓全部的 Activity,在打開的 Activity#onCreate 中把 ActivityApplicationLocale 都設置爲咱們設置的語言便可,固然,這份設置是須要保存下來的,根據你的需求來肯定是要保存在服務端仍是本地。相關代碼以下所示:微信

private static void applyLanguageReal(final Locale locale, final boolean isRelaunchApp) {
    if (locale == null) {
    	// 後面的 true 使用的是 sp.commit,由於若是用 apply 的話,殺掉應用重啓會來不及保存
        UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, VALUE_FOLLOW_SYSTEM, true);
    } else {
        UtilsBridge.getSpUtils4Utils().put(KEY_LOCALE, locale2String(locale), true);
    }
    Locale destLocal = locale == null ? getLocal(Resources.getSystem().getConfiguration()) : locale;
    // 這個就是上面提到的函數
    updateAppContextLanguage(destLocal, new Utils.Consumer<Boolean>() {
        @Override
        public void accept(Boolean success) {
            if (success) {
                restart(isRelaunchApp);
            } else {
                // use relaunch app
                UtilsBridge.relaunchApp();
            }
        }
    });
}

private static void restart(final boolean isRelaunchApp) {
    if (isRelaunchApp) {
        UtilsBridge.relaunchApp();
    } else {
        for (Activity activity : UtilsBridge.getActivityList()) {
            activity.recreate();
        }
    }
}

// 工具類調用此函數是在 ActivityLifecycleCallbacks#onActivityCreated 中
static void applyLanguage(final Activity activity) {
    String spLocale = UtilsBridge.getSpUtils4Utils().getString(KEY_LOCALE);
    if (TextUtils.isEmpty(spLocale)) {
        return;
    }
    Locale destLocal;
    if (VALUE_FOLLOW_SYSTEM.equals(spLocale)) {
        destLocal = getLocal(Resources.getSystem().getConfiguration());
    } else {
        destLocal = string2Locale(spLocale);
    }
    if (destLocal == null) return;
    updateConfiguration(activity, destLocal);
    updateConfiguration(Utils.getApp(), destLocal);
}

private static void updateConfiguration(Context context, Locale destLocal) {
    Resources resources = context.getResources();
    Configuration config = resources.getConfiguration();
    setLocal(config, destLocal);
    resources.updateConfiguration(config, resources.getDisplayMetrics());
}
複製代碼

基於以上分析:markdown

  • 若是應用是跟隨系統設置語言來切換的話,那麼直接依賴個人工具類便可,它會自動幫你更新 Application 的語言。
  • 若是須要應用內切換語言的話,只需在你切換語言的地方調用 LanguageUtils.applyLanguage(Locale.你要設置的語言[, false]); // 第二個參數可選擇性傳入,表明是否要重啓應用,false 的話是 recreate 全部 Activity,true 的話就是重啓應用 便可;
  • 若是須要應用內切換語言變爲跟隨系統設置語言,那麼調用 LanguageUtils.applySystemLanguage([false]); // 參數和上面說的是否重啓應用同樣 便可。

結語

功能其實很簡單,但老是缺乏人能把它分析得透徹,從而作得很完美分享出來,但願我此次的分享能讓你看到這一點,從而提高你以後的技能。app

字節跳動大量招人中,歡迎向 blankj@qq.com 投遞您的簡歷。ide

相關文章
相關標籤/搜索