Android context.getResources.updateConfiguration () 지원 중단됨
최근에 context.getResources (). updateConfiguration () 은 Android API 25에서 더 이상 사용되지 않으며 컨텍스트를 사용하는 것이 좋습니다. 대신 createConfigurationContext () .
누구든지 createConfigurationContext 를 사용하여 안드로이드 시스템 로케일을 재정의 하는 방법을 알고 있습니까?
이 작업을 수행하기 전에 :
Configuration config = getBaseContext().getResources().getConfiguration();
config.setLocale(locale);
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
Calligraphy 에서 영감을 받아 컨텍스트 래퍼를 만들었습니다. 제 경우에는 앱 사용자에게 앱 언어 변경 옵션을 제공하기 위해 시스템 언어를 덮어 써야하지만 구현해야하는 모든 로직으로 사용자 지정할 수 있습니다.
import android.annotation.TargetApi;
import android.content.Context;
import android.content.ContextWrapper;
import android.content.res.Configuration;
import android.os.Build;
import java.util.Locale;
public class MyContextWrapper extends ContextWrapper {
public MyContextWrapper(Context base) {
super(base);
}
@SuppressWarnings("deprecation")
public static ContextWrapper wrap(Context context, String language) {
Configuration config = context.getResources().getConfiguration();
Locale sysLocale = null;
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
sysLocale = getSystemLocale(config);
} else {
sysLocale = getSystemLocaleLegacy(config);
}
if (!language.equals("") && !sysLocale.getLanguage().equals(language)) {
Locale locale = new Locale(language);
Locale.setDefault(locale);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
setSystemLocale(config, locale);
} else {
setSystemLocaleLegacy(config, locale);
}
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
context = context.createConfigurationContext(config);
} else {
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
}
return new MyContextWrapper(context);
}
@SuppressWarnings("deprecation")
public static Locale getSystemLocaleLegacy(Configuration config){
return config.locale;
}
@TargetApi(Build.VERSION_CODES.N)
public static Locale getSystemLocale(Configuration config){
return config.getLocales().get(0);
}
@SuppressWarnings("deprecation")
public static void setSystemLocaleLegacy(Configuration config, Locale locale){
config.locale = locale;
}
@TargetApi(Build.VERSION_CODES.N)
public static void setSystemLocale(Configuration config, Locale locale){
config.setLocale(locale);
}
}
래퍼를 삽입하려면 모든 활동에 다음 코드를 추가하십시오.
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(MyContextWrapper.wrap(newBase,"fr"));
}
업데이트 2018 년 10 월 19 일 때때로 방향 변경 또는 활동 일시 중지 / 재개 후 Configuration 개체가 기본 시스템 구성으로 재설정되고 결과적으로 프랑스어 "fr"로케일로 컨텍스트를 래핑하더라도 앱에 영어 "en"텍스트가 표시됩니다. . 따라서 좋은 방법으로 활동 또는 조각의 전역 변수에 Context / Activity 개체를 유지하지 마십시오.
또한 MyBaseFragment 또는 MyBaseActivity에서 다음을 만들고 사용합니다.
public Context getMyContext(){
return MyContextWrapper.wrap(getContext(),"fr");
}
이 연습은 100 % 버그없는 솔루션을 제공합니다.
아마도 다음과 같습니다.
Configuration overrideConfiguration = getBaseContext().getResources().getConfiguration();
overrideConfiguration.setLocales(LocaleList);
Context context = createConfigurationContext(overrideConfiguration);
Resources resources = context.getResources();
보너스 : createConfigurationContext ()를 사용하는 블로그 기사
Calligraphy & Mourjan & 나 자신에게 영감을 받아 이것을 만들었습니다.
먼저 Application의 하위 클래스를 만들어야합니다.
public class MyApplication extends Application {
private Locale locale = null;
@Override
public void onCreate() {
super.onCreate();
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
Configuration config = getBaseContext().getResources().getConfiguration();
String lang = preferences.getString(getString(R.string.pref_locale), "en");
String systemLocale = getSystemLocale(config).getLanguage();
if (!"".equals(lang) && !systemLocale.equals(lang)) {
locale = new Locale(lang);
Locale.setDefault(locale);
setSystemLocale(config, locale);
updateConfiguration(config);
}
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (locale != null) {
setSystemLocale(newConfig, locale);
Locale.setDefault(locale);
updateConfiguration(newConfig);
}
}
@SuppressWarnings("deprecation")
private static Locale getSystemLocale(Configuration config) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return config.getLocales().get(0);
} else {
return config.locale;
}
}
@SuppressWarnings("deprecation")
private static void setSystemLocale(Configuration config, Locale locale) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(locale);
} else {
config.locale = locale;
}
}
@SuppressWarnings("deprecation")
private void updateConfiguration(Configuration config) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
getBaseContext().createConfigurationContext(config);
} else {
getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
}
}
}
그런 다음이를 AndroidManifest.xml 애플리케이션 태그로 설정해야합니다.
<application
...
android:name="path.to.your.package.MyApplication"
>
이를 AndroidManifest.xml 활동 태그에 추가하십시오.
<activity
...
android:configChanges="locale"
>
pref_locale은 다음과 같은 문자열 리소스입니다.
<string name="pref_locale">fa</string>
pref_locale이 설정되지 않은 경우 하드 코드 "en"은 기본 언어입니다.
다음은 약간의 kotlin 장점을 가진 @ bassel-mourjan의 솔루션입니다 :) :
import android.annotation.TargetApi
import android.content.ContextWrapper
import android.os.Build
import java.util.*
@Suppress("DEPRECATION")
fun ContextWrapper.wrap(language: String): ContextWrapper {
val config = baseContext.resources.configuration
val sysLocale: Locale = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.getSystemLocale()
} else {
this.getSystemLocaleLegacy()
}
if (!language.isEmpty() && sysLocale.language != language) {
val locale = Locale(language)
Locale.setDefault(locale)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
this.setSystemLocale(locale)
} else {
this.setSystemLocaleLegacy(locale)
}
}
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
val context = baseContext.createConfigurationContext(config)
ContextWrapper(context)
} else {
baseContext.resources.updateConfiguration(config, baseContext.resources.displayMetrics)
ContextWrapper(baseContext)
}
}
@Suppress("DEPRECATION")
fun ContextWrapper.getSystemLocaleLegacy(): Locale {
val config = baseContext.resources.configuration
return config.locale
}
@TargetApi(Build.VERSION_CODES.N)
fun ContextWrapper.getSystemLocale(): Locale {
val config = baseContext.resources.configuration
return config.locales[0]
}
@Suppress("DEPRECATION")
fun ContextWrapper.setSystemLocaleLegacy(locale: Locale) {
val config = baseContext.resources.configuration
config.locale = locale
}
@TargetApi(Build.VERSION_CODES.N)
fun ContextWrapper.setSystemLocale(locale: Locale) {
val config = baseContext.resources.configuration
config.setLocale(locale)
}
사용 방법은 다음과 같습니다.
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(ContextWrapper(newBase).wrap(defaultLocale.language))
}
이 시도:
Configuration config = getBaseContext().getResources().getConfiguration();
config.setLocale(locale);
context.createConfigurationContext(config);
contextWrapper를 사용한 간단한 솔루션이 있습니다. Android N 프로그래밍 방식으로 언어 변경 recreate () 메서드에주의
100 % 작동하는 솔루션은 없습니다. 당신은 모두를 사용해야 createConfigurationContext
하고 applyOverrideConfiguration
. 그렇지 않으면 baseContext
모든 활동을 새 구성으로 바꾸더라도 활동은 이전 로케일로 Resources
from ContextThemeWrapper
을 계속 사용 합니다.
그래서 여기에 API 29까지 작동하는 내 솔루션이 있습니다.
애플리케이션 클래스에서 :
public class MainApplication extends Application {
protected void attachBaseContext(Context base) {
super.attachBaseContext(LocaleHelper.changeLanguageIfDiff(
base, PreferenceManager.getDefaultSharedPreferences(base).getString("langPref", "sys")));
}
}
또한 모든 활동에서 :
public class MainActivity extends AppCompatActivity {
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(LocaleHelper.changeLanguageIfDiff(
newBase, PreferenceManager.getDefaultSharedPreferences(base).getString("langPref", "sys")));
}
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
super.applyOverrideConfiguration(getBaseContext().getResources().getConfiguration());
}
}
그리고 여기 있습니다 LocaleHelper
:
public class LocaleHelper {
public static boolean isAppLangDiff(final Context context, final String prefLang) {
final Configuration appConfig = context.getResources().getConfiguration();
final Configuration sysConfig = Resources.getSystem().getConfiguration();
final String appLang = getLocale(appConfig).getLanguage();
final String sysLang = getLocale(sysConfig).getLanguage();
if ("sys".equals(prefLang)) { // "System default" preference value
return !appLang.equals(sysLang);
} else {
return !appLang.equals(prefLang);
}
}
public static Context changeLanguageIfDiff(final Context context, final String prefLang) {
return isAppLangDiff(context, prefLang)
? changeLanguage(context, prefLang)
: context;
}
public static Context changeLanguage(final Context context, final String toLang) {
final Resources res = context.getResources();
final Configuration config = res.getConfiguration();
// Here some workaround with Chinese Simplified/Traditional
final Locale toLocale = toLang.contains("zh")
? (toLang.contains("rCN")
? Locale.SIMPLIFIED_CHINESE : Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? new Locale("zh", "Hant") : Locale.TRADITIONAL_CHINESE)
: new Locale(toLang);
Locale.setDefault(toLocale);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(toLocale);
final LocaleList localeList = new LocaleList(toLocale);
LocaleList.setDefault(localeList);
config.setLocales(localeList);
} else {
config.locale = toLocale;
}
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
config.setLayoutDirection(toLocale);
return context.createConfigurationContext(config);
} else {
res.updateConfiguration(config, res.getDisplayMetrics());
return context;
}
}
public static Locale getLocale(Configuration config) {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N
? config.getLocales().get(0)
: config.locale;
}
}
I use next language values for preference:
<string-array name="lang_values" translatable="false">
<item>sys</item> <!-- System default -->
<item>ar</item>
<item>de</item>
<item>en</item>
<item>es</item>
<item>fa</item>
...
<item>zh</item> <!-- Traditional Chinese -->
<item>zh-rCN</item> <!-- Simplified Chinese -->
</string-array>
I want to mention:
- Use
config.setLayoutDirection(toLocale);
to change layout direction when you use RTL locales like Arabic, Persian, etc. "sys"
in the code is a value that means "inherit system default language".- There is no need to recreate the context if it already uses needed locale.
- There is no need for
ContextWraper
as posted here, just set new context returned fromcreateConfigurationContext
as baseContext - It is not enough only to
recreate
activity when user selects a different language, because applicationContext will remain with old locale and it could provide unexpected behaviour. So listen to preference change and restart whole application task instead:
public static void recreateTask(final Context context) {
final PackageManager pm = context.getPackageManager();
final Intent intent = pm.getLaunchIntentForPackage(context.getPackageName());
final ComponentName componentName = intent.getComponent();
final Intent mainIntent = Intent.makeRestartActivityTask(componentName);
context.startActivity(mainIntent);
Runtime.getRuntime().exit(0);
}
'Programing' 카테고리의 다른 글
Java에 브라우저를 임베드하는 방법이 있습니까? (0) | 2020.11.12 |
---|---|
데이터 목록과 함께 Python에서 Matplotlib를 사용하여 히스토그램을 그리는 방법은 무엇입니까? (0) | 2020.11.11 |
일반 텍스트에서 가장 적게 사용 된 구분 문자 <ASCII 128 (0) | 2020.11.11 |
xpath를 사용하여 노드의 N 번째 자식 가져 오기 (0) | 2020.11.11 |
/ dev / urandom을 이해 했나요? (0) | 2020.11.11 |