背景
之前總是有開發者回報我應用切換了語言,可是工具類擷取的
string
卻沒有發生改變。其實這個問題很簡單,你切換語言的
Context
隻作用在了你的
Activity
上,并沒有對你的
Application
做同樣的操作,知道了這點,那麼解決問題就很簡單了,為了省事,我給大家封裝了
LanguageUtils
,直接一行代碼便可完成多語言的切換,類似微信的語言切換分分鐘便可完成。
使用
Gradle:
implementation 'com.blankj:utilcode:latest_version'
APIs
語言相關
applySystemLanguage: 應用系統語言
applyLanguage : 應用語言
原理
如果我們的應用不設定
android:configChanges="locale|layoutDirection"
,那麼應用是跟随系統語言設定的變化而變化的,比如你應用适配了英語(
values-en-rUS
)和簡體中文(
values-zh-rCN
),那麼你去設定裡切換成英語的話,傳回到你應用中,你的
Activity
會重新建立一遍,把
Activity#Resource#Configuration#Locale
設定為目前系統語言,這樣就達到了跟随系統語言設定的變化而變化,但
Application
并沒有重新開機,是以這就導緻了一開說到的問題。
要解決跟随系統變化這一點的話,隻需要在
Activity#onCreate
的生命周期中把
Application#Resource#Configuration#Locale
設定為系統的
Locale
即可,那麼系統的
Locale
怎麼讀取呢,知道之前螢幕适配方案的人應該也能想到這一方式:
Resources.getSystem().getConfiguration().locale
,這樣,我們的
Application
便也切換成了系統語言,注意更新
Locale
需要相容下高版本,調用具體代碼可以參照如下:
private static void updateLanguage(Context context, Locale locale) {
Resources resources = context.getResources();
Configuration config = resources.getConfiguration();
Locale contextLocale = config.locale;
if (equals(contextLocale.getLanguage(), locale.getLanguage())
&& equals(contextLocale.getCountry(), locale.getCountry())) {
return;
}
DisplayMetrics dm = resources.getDisplayMetrics();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
config.setLocale(locale);
context.createConfigurationContext(config);
} else {
config.locale = locale;
}
resources.updateConfiguration(config, dm);
}
那麼如果是應用内切換語言呢?我們可以仿照系統切換語言的方式,把我們自己所有的
Activity
全關掉,然後啟動首頁的
Activity
即可,在打開的
Activity#onCreate
中把
Activity
和
Application
的
Locale
都設定為我們設定的語言即可,當然,這份設定是需要儲存下來的,根據你的需求來确定是要儲存在服務端還是本地。那麼怎麼關閉所有的
Activity
呢?我們可以通過增加
Intent
的 flag 為
Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
即可,相關代碼如下所示:
private static void applyLanguage(@NonNull final Locale locale,
final String activityClassName,
final boolean isFollowSystem) {
if (isFollowSystem) {// 如果是跟随系統,那麼 sp 就什麼都不存
SPUtils.getInstance().put(KEY_LOCALE, "");
} else {// 否則把設定的語言儲存下來,在 onCreate 中應用該語言
String localLanguage = locale.getLanguage();
String localCountry = locale.getCountry();
SPUtils.getInstance().put(KEY_LOCALE, localLanguage + "$" + localCountry);
}
updateLanguage(Utils.getApp(), locale);// 更新 Application 的語言
Intent intent = new Intent();
String realActivityClassName// 如果傳入的 activityClassName 為空,那麼啟動 launcher Activity。
= TextUtils.isEmpty(activityClassName) ? getLauncherActivity() : activityClassName;
intent.setComponent(new ComponentName(Utils.getApp(), realActivityClassName));
intent.addFlags(
Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK
);
Utils.getApp().startActivity(intent);// 關閉其他 Activity 并啟動 realActivityClassName 的 Activity
}
// 工具類調用此函數是在 ActivityLifecycleCallbacks#onActivityCreated 中
static void applyLanguage(@NonNull final Activity activity) {
final String spLocale = SPUtils.getInstance().getString(KEY_LOCALE);// 擷取儲存的語言
if (TextUtils.isEmpty(spLocale)) {// 為空說明是跟随系統走,那麼更新系統語言即可
Locale sysLocale = Resources.getSystem().getConfiguration().locale;
updateLanguage(Utils.getApp(), sysLocale);
updateLanguage(activity, sysLocale);
return;
}
// 讀取 sp 儲存下來的語言并應用該語言
String[] language_country = spLocale.split("\\$");
if (language_country.length != 2) {
Log.e("LanguageUtils", "The string of " + spLocale + " is not in the correct format.");
return;
}
Locale settingLocale = new Locale(language_country[0], language_country[1]);
updateLanguage(Utils.getApp(), settingLocale);
updateLanguage(activity, settingLocale);
}
基于以上分析:
- 如果應用是跟随系統設定語言來切換的話,那麼直接依賴我的工具類即可,它會自動幫你更新
的語言。Application
- 如果需要應用内切換語言的話,隻需在你切換語言的地方調用
即可;LanguageUtils.applyLanguage(Locale.你要設定的語言, "com.blankj.launcher.pkg.MainActivity")
- 如果需要應用内切換語言變為跟随系統設定語言,那麼調用
即可。LanguageUtils.applySystemLanguage("");
結語
功能其實很簡單,但總是缺少人能把它分析得透徹,進而做得很完美分享出來,希望我這次的分享能讓你看到這一點,進而提升你之後的技能。
最後
如果你看到了這裡,覺得文章寫得不錯就給個贊呗!歡迎大家評論讨論!如果你覺得那裡值得改進的,請給我留言。一定會認真查詢,修正不足,定期免費分享技術幹貨。謝謝!