Erst kürzlich context.getResources (). updateConfiguration () ist in der Android API 25 veraltet und es wird empfohlen, den Kontext zu verwenden. stattdessen createConfigurationContext () .
Weiß jemand, wie createConfigurationContext verwendet werden kann, um das Gebietsschema des Android-Systems zu überschreiben?
bevor dies geschehen würde durch:
Configuration config = getBaseContext().getResources().getConfiguration();
config.setLocale(locale);
context.getResources().updateConfiguration(config,
context.getResources().getDisplayMetrics());
android
locale
right-to-left
Bassel Mourjan
quelle
quelle
Antworten:
Inspiriert von der Kalligraphie habe ich schließlich einen Kontext-Wrapper erstellt. In meinem Fall muss ich die Systemsprache überschreiben, um meinen App-Benutzern die Möglichkeit zu geben, die App-Sprache zu ändern. Dies kann jedoch mit jeder Logik angepasst werden, die Sie implementieren müssen.
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); } }
Um Ihren Wrapper zu injizieren, fügen Sie in jeder Aktivität den folgenden Code hinzu:
@Override protected void attachBaseContext(Context newBase) { super.attachBaseContext(MyContextWrapper.wrap(newBase,"fr")); }
UPDATE 23/09/2020 Wenn Sie beispielsweise das App-Thema überschreiben, um beispielsweise den Dunkelmodus anzuwenden, wird ContextThemeWrapper die Spracheinstellung aufheben. Fügen Sie daher Ihrer Aktivität den folgenden Code hinzu, um das gewünschte Gebietsschema zurückzusetzen
@Override public void applyOverrideConfiguration(Configuration overrideConfiguration) { Locale locale = new Locale("fr"); overrideConfiguration.setLocale(locale); super.applyOverrideConfiguration(overrideConfiguration); }
UPDATE 19.10.2008 Manchmal wird das Konfigurationsobjekt nach einer Änderung der Ausrichtung oder einer Pause / Wiederaufnahme der Aktivität auf die Standardsystemkonfiguration zurückgesetzt. Im Ergebnis wird in der App englischer "en" -Text angezeigt, obwohl der Kontext mit dem französischen Gebietsschema "fr" umbrochen wurde . Behalten Sie daher das Context / Activity-Objekt in einer globalen Variablen in Aktivitäten oder Fragmenten niemals als bewährte Methode bei.
Erstellen und verwenden Sie außerdem Folgendes in MyBaseFragment oder MyBaseActivity:
public Context getMyContext(){ return MyContextWrapper.wrap(getContext(),"fr"); }
Diese Vorgehensweise bietet Ihnen eine 100% fehlerfreie Lösung.
quelle
Wahrscheinlich so:
Configuration overrideConfiguration = getBaseContext().getResources().getConfiguration(); overrideConfiguration.setLocales(LocaleList); Context context = createConfigurationContext(overrideConfiguration); Resources resources = context.getResources();
Bonus: Ein Blog-Artikel, der createConfigurationContext () verwendet
quelle
LocaleList
Inspiriert von Calligraphy & Mourjan & mir habe ich das erstellt.
Zuerst müssen Sie eine Unterklasse der Anwendung erstellen:
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()); } } }
Dann müssen Sie dies auf Ihr AndroidManifest.xml-Anwendungstag setzen:
<application ... android:name="path.to.your.package.MyApplication" >
und fügen Sie dies Ihrem Aktivitäts-Tag AndroidManifest.xml hinzu.
<activity ... android:configChanges="locale" >
Beachten Sie, dass pref_locale eine Zeichenfolgenressource wie diese ist:
<string name="pref_locale">fa</string>
und der Hardcode "en" ist die Standardsprache, wenn pref_locale nicht festgelegt ist
quelle
Hier gibt es keine 100% funktionierende Lösung. Sie müssen beide
createConfigurationContext
und verwendenapplyOverrideConfiguration
. Ansonsten , auch wenn Sie ersetzenbaseContext
bei jeder Tätigkeit mit neuer Konfiguration würde Aktivität noch verwendetResources
ausContextThemeWrapper
mit alten locale.Hier ist meine Lösung, die bis zu API 29 funktioniert:
Unterklassifizieren Sie Ihre
MainApplication
Klasse von:abstract class LocalApplication : Application() { override fun attachBaseContext(base: Context) { super.attachBaseContext( base.toLangIfDiff( PreferenceManager .getDefaultSharedPreferences(base) .getString("langPref", "sys")!! ) ) } }
Auch jeder
Activity
von: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) } }
In
LocaleExt.kt
mit dem nächsten Erweiterungsfunktionen: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 }
Fügen Sie
res/values/arrays.xml
Ihren unterstützten Sprachen im Array Folgendes hinzu:<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>
Ich möchte erwähnen:
config.setLayoutDirection(toLocale);
Sie diese Option, um die Layoutrichtung zu ändern, wenn Sie RTL-Gebietsschemas wie Arabisch, Persisch usw. verwenden."sys"
im Code ist ein Wert, der "Systemstandardsprache erben" bedeutet.ContextWraper
wie hier veröffentlicht,createConfigurationContext
sondern nur den neuen Kontext festzulegen, der als baseContext zurückgegeben wirdcreateConfigurationContext
, sollten Sie die Konfiguration von Grund auf neu und nur mit festgelegtenLocale
Eigenschaften übergeben. Für diese Konfiguration sollte keine andere Eigenschaft festgelegt sein. Denn wenn wir einige andere Eigenschaften für diese Konfiguration festlegen ( z. B. Ausrichtung ), überschreiben wir diese Eigenschaft für immer, und unser Kontext ändert diese Ausrichtungseigenschaft nicht mehr, selbst wenn wir den Bildschirm drehen.recreate
aktivieren, wenn der Benutzer eine andere Sprache auswählt, da applicationContext im alten Gebietsschema verbleibt und unerwartetes Verhalten hervorrufen kann. Hören Sie sich also die Änderung der Einstellungen an und starten Sie stattdessen die gesamte Anwendungsaufgabe neu:fun Context.recreateTask() { this.packageManager .getLaunchIntentForPackage(context.packageName) ?.let { intent -> val restartIntent = Intent.makeRestartActivityTask(intent.component) this.startActivity(restartIntent) Runtime.getRuntime().exit(0) } }
quelle
recreateTask(Context context)
Außerdem funktioniert die Methode nicht richtig, da ich das Layout immer noch ohne Änderung sehe.Hier ist @ bassel-mourjans Lösung mit ein bisschen Kotlin-Güte :):
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) }
Und so verwenden Sie es:
override fun attachBaseContext(newBase: Context?) { super.attachBaseContext(ContextWrapper(newBase).wrap(defaultLocale.language)) }
quelle
val config = baseContext.resources.configuration
ist sehr sehr falsch. Sie werden deshalb viele Fehler haben. Sie müssen stattdessen eine neue Konfiguration erstellen. Siehe meine Antwort.Hier gibt es eine einfache Lösung mit contextWrapper: Android N ändert die Sprache programmgesteuert. Achten Sie auf die Methode recreate ()
quelle
Versuche dies:
Configuration config = getBaseContext().getResources().getConfiguration(); config.setLocale(locale); context.createConfigurationContext(config);
quelle