Niedawno context.getResources (). updateConfiguration () jest przestarzała w Android API 25 i zaleca się używanie kontekstu. Zamiast tego createConfigurationContext () .
Czy ktoś wie, jak można użyć createConfigurationContext do zastąpienia ustawień regionalnych systemu Android?
zanim zostanie to zrobione przez:
Configuration config = getBaseContext().getResources().getConfiguration();
config.setLocale(locale);
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
android
locale
right-to-left
Bassel Mourjan
źródło
źródło
Odpowiedzi:
Zainspirowany kaligrafią , w końcu stworzyłem opakowanie kontekstowe. W moim przypadku muszę nadpisać język systemu, aby zapewnić użytkownikom mojej aplikacji możliwość zmiany języka aplikacji, ale można to dostosować za pomocą dowolnej logiki, którą trzeba wdrożyć.
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.N) { 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); } }
i aby wstrzyknąć opakowanie, w każdym działaniu dodaj następujący kod:
@Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(MyContextWrapper.wrap(newBase,"fr")); }
AKTUALIZACJA 23/09/2020 W przypadku nadpisania motywu aplikacji, aby na przykład zastosować tryb ciemny, ContextThemeWrapper złamie ustawienie języka, dlatego dodaj następujący kod do swojej aktywności, aby zresetować żądane ustawienia regionalne
@Override public void applyOverrideConfiguration(Configuration overrideConfiguration) { Locale locale = new Locale("fr"); overrideConfiguration.setLocale(locale); super.applyOverrideConfiguration(overrideConfiguration); }
AKTUALIZACJA 19/10/2018 Czasami po zmianie orientacji lub wstrzymaniu / wznowieniu działania obiekt konfiguracyjny resetuje się do domyślnej konfiguracji systemu, w wyniku czego aplikacja wyświetla angielski tekst „en”, mimo że kontekst został opakowany francuskim ustawieniem „fr” . Dlatego i jako dobrą praktykę, nigdy nie przechowuj obiektu Context / Activity w zmiennej globalnej w działaniach lub fragmentach.
ponadto utwórz i użyj następujących elementów w MyBaseFragment lub MyBaseActivity:
public Context getMyContext(){ return MyContextWrapper.wrap(getContext(),"fr"); }
Ta praktyka zapewni Ci w 100% wolne od błędów rozwiązanie.
źródło
Prawdopodobnie tak:
Configuration overrideConfiguration = getBaseContext().getResources().getConfiguration(); overrideConfiguration.setLocales(LocaleList); Context context = createConfigurationContext(overrideConfiguration); Resources resources = context.getResources();
Bonus: artykuł na blogu, który używa funkcji createConfigurationContext ()
źródło
LocaleList
Zainspirowany Calligraphy & Mourjan & mną, stworzyłem to.
najpierw musisz utworzyć podklasę 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()); } } }
następnie musisz ustawić to na swój tag aplikacji AndroidManifest.xml:
<application ... android:name="path.to.your.package.MyApplication" >
i dodaj to do swojego tagu aktywności AndroidManifest.xml.
<activity ... android:configChanges="locale" >
zwróć uwagę, że pref_locale to zasób w postaci ciągu:
<string name="pref_locale">fa</string>
a hardcode „en” jest domyślnym językiem, jeśli pref_locale nie jest ustawione
źródło
Tutaj nie ma 100% działającego rozwiązania. Musisz użyć obu
createConfigurationContext
iapplyOverrideConfiguration
. W przeciwnym razie, nawet jeśli zastąpićbaseContext
w każdej działalności z nową konfiguracją, aktywność będzie nadal korzystaćResources
zContextThemeWrapper
ze starej lokalizacji.Oto moje rozwiązanie, które działa do API 29:
Podklasuj swoją
MainApplication
klasę z:abstract class LocalApplication : Application() { override fun attachBaseContext(base: Context) { super.attachBaseContext( base.toLangIfDiff( PreferenceManager .getDefaultSharedPreferences(base) .getString("langPref", "sys")!! ) ) } }
Również każdy
Activity
z:abstract class LocalActivity : AppCompatActivity() { override fun attachBaseContext(newBase: Context) { super.attachBaseContext( PreferenceManager .getDefaultSharedPreferences(base) .getString("langPref", "sys")!! ) } override fun applyOverrideConfiguration(overrideConfiguration: Configuration) { super.applyOverrideConfiguration(baseContext.resources.configuration) } }
Dodaj
LocaleExt.kt
z następnymi funkcjami rozszerzenia:const val SYSTEM_LANG = "sys" const val ZH_LANG = "zh" const val SIMPLIFIED_CHINESE_SUFFIX = "rCN" private fun Context.isAppLangDiff(prefLang: String): Boolean { val appConfig: Configuration = this.resources.configuration val sysConfig: Configuration = Resources.getSystem().configuration val appLang: String = appConfig.localeCompat.language val sysLang: String = sysConfig.localeCompat.language return if (SYSTEM_LANG == prefLang) { appLang != sysLang } else { appLang != prefLang || ZH_LANG == prefLang } } fun Context.toLangIfDiff(lang: String): Context = if (this.isAppLangDiff(lang)) { this.toLang(lang) } else { this } @Suppress("DEPRECATION") fun Context.toLang(toLang: String): Context { val config = Configuration() val toLocale = langToLocale(toLang) Locale.setDefault(toLocale) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { config.setLocale(toLocale) val localeList = LocaleList(toLocale) LocaleList.setDefault(localeList) config.setLocales(localeList) } else { config.locale = toLocale } return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { config.setLayoutDirection(toLocale) this.createConfigurationContext(config) } else { this.resources.updateConfiguration(config, this.resources.displayMetrics) this } } /** * @param toLang - two character representation of language, could be "sys" - which represents system's locale */ fun langToLocale(toLang: String): Locale = when { toLang == SYSTEM_LANG -> Resources.getSystem().configuration.localeCompat toLang.contains(ZH_LANG) -> when { toLang.contains(SIMPLIFIED_CHINESE_SUFFIX) -> Locale.SIMPLIFIED_CHINESE Build.VERSION.SDK_INT >= Build.VERSION_CODES.N -> Locale(ZH_LANG, "Hant") else -> Locale.TRADITIONAL_CHINESE } else -> Locale(toLang) } @Suppress("DEPRECATION") private val Configuration.localeCompat: Locale get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { this.locales.get(0) } else { this.locale }
Dodaj do
res/values/arrays.xml
obsługiwanych języków w tablicy:<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>
Chcę wspomnieć:
config.setLayoutDirection(toLocale);
do zmiany kierunku układu podczas korzystania z ustawień regionalnych RTL, takich jak arabski, perski itp."sys"
w kodzie jest wartością oznaczającą „dziedziczenie domyślnego języka systemu”.ContextWraper
tutaj, wystarczy ustawić nowy kontekst zwrócony zcreateConfigurationContext
jako baseContextcreateConfigurationContext
, powinieneś przekazać konfigurację utworzoną od zera i tylko zLocale
ustawioną właściwością. W tej konfiguracji nie powinno być żadnych innych właściwości. Ponieważ jeśli ustawimy inne właściwości dla tej konfiguracji ( na przykład orientację ), nadpisujemy tę właściwość na zawsze, a nasz kontekst nie zmienia już tej właściwości orientacji, nawet jeśli obrócimy ekran.recreate
działać, gdy użytkownik wybierze inny język, ponieważ applicationContext pozostanie ze starymi ustawieniami lokalnymi i może zapewnić nieoczekiwane zachowanie. Więc posłuchaj zmiany preferencji i zamiast tego zrestartuj całe zadanie aplikacji:fun Context.recreateTask() { this.packageManager .getLaunchIntentForPackage(context.packageName) ?.let { intent -> val restartIntent = Intent.makeRestartActivityTask(intent.component) this.startActivity(restartIntent) Runtime.getRuntime().exit(0) } }
źródło
recreateTask(Context context)
metoda nie działa poprawnie, ponieważ nadal widzę układ bez żadnych zmian.Oto rozwiązanie @ bassel-mourjan z odrobiną dobroci kotlina :):
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) }
A oto jak tego używasz:
override fun attachBaseContext(newBase: Context?) { super.attachBaseContext(ContextWrapper(newBase).wrap(defaultLocale.language)) }
źródło
val config = baseContext.resources.configuration
jest bardzo błędna. Skończysz z wieloma błędami z tego powodu. Zamiast tego musisz utworzyć nową konfigurację. Zobacz moją odpowiedź.jest tutaj proste rozwiązanie z contextWrapper: Android N zmienia język programowo Zwróć uwagę na metodęrereate ()
źródło
Spróbuj tego:
Configuration config = getBaseContext().getResources().getConfiguration(); config.setLocale(locale); context.createConfigurationContext(config);
źródło