Ich fand einen wirklich seltsamen Fehler, der nur auf Android N-Geräten reproduziert wird.
In der Tour meiner App gibt es eine Möglichkeit, die Sprache zu wechseln. Hier ist der Code, der es ändert.
public void update(Locale locale) {
Locale.setDefault(locale);
Configuration configuration = res.getConfiguration();
if (BuildUtils.isAtLeast24Api()) {
LocaleList localeList = new LocaleList(locale);
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
configuration.setLocale(locale);
} else if (BuildUtils.isAtLeast17Api()){
configuration.setLocale(locale);
} else {
configuration.locale = locale;
}
res.updateConfiguration(configuration, res.getDisplayMetrics());
}
Dieser Code funktioniert gut in der Aktivität meiner Tour (mit recreate()
call), aber bei allen nächsten Aktivitäten sind alle String-Ressourcen falsch. Bildschirmdrehung behebt das Problem. Was kann ich mit diesem Problem anfangen? Sollte ich die Ländereinstellung für Android N anders ändern, oder ist es nur ein Systemfehler?
P.S. Hier ist was ich gefunden habe. Beim ersten Start von MainActivity (was nach meiner Tour ist) ist Locale.getDefault()
korrekt, aber die Ressourcen sind falsch. Bei anderen Aktivitäten gibt es jedoch falsche Gebietsschemas und falsche Ressourcen aus diesem Gebietsschema. Nach dem Drehen des Bildschirms (oder einer anderen Konfigurationsänderung) ist Locale.getDefault()
korrekt.
OK. Endlich habe ich eine Lösung gefunden.
Zunächst sollten Sie wissen, dass in 25 API Resources.updateConfiguration(...)
veraltet ist. Stattdessen kannst du so etwas tun:
1) Sie müssen einen eigenen ContextWrapper erstellen, der alle Konfigurationsparameter in baseContext überschreibt. Dies ist beispielsweise mein ContextWrapper, der das Gebietsschema korrekt ändert. Beachten Sie die context.createConfigurationContext(configuration)
-Methode.
public class ContextWrapper extends Android.content.ContextWrapper {
public ContextWrapper(Context base) {
super(base);
}
public static ContextWrapper wrap(Context context, Locale newLocale) {
Resources res = context.getResources();
Configuration configuration = res.getConfiguration();
if (BuildUtils.isAtLeast24Api()) {
configuration.setLocale(newLocale);
LocaleList localeList = new LocaleList(newLocale);
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
context = context.createConfigurationContext(configuration);
} else if (BuildUtils.isAtLeast17Api()) {
configuration.setLocale(newLocale);
context = context.createConfigurationContext(configuration);
} else {
configuration.locale = newLocale;
res.updateConfiguration(configuration, res.getDisplayMetrics());
}
return new ContextWrapper(context);
}}
2) Was Sie in Ihrer BaseActivity tun sollten:
@Override
protected void attachBaseContext(Context newBase) {
Locale newLocale;
// .. create or get your new Locale object here.
Context context = ContextWrapper.wrap(newBase, newLocale);
super.attachBaseContext(context);
}
Hinweis:
Denken Sie daran, Ihre Aktivität neu zu erstellen, wenn Sie das Gebietsschema in .__ ändern möchten. Deine App irgendwo. Sie können jede gewünschte Konfiguration mit .__ überschreiben. diese Lösung.
Inspiriert von verschiedenen Codes (d. H. Unseren Stackoverflow-Brüdern), hatte ich eine viel einfachere Version erstellt. Die Erweiterung ContextWrapper
ist nicht erforderlich.
Nehmen wir an, Sie haben 2 Knöpfe für 2 Sprachen, EN und KH. Speichern Sie im onClick für die Schaltflächen den Sprachcode in SharedPreferences
und rufen Sie dann die Activity recreate()
-Methode auf.
Beispiel:
@Override
public void onClick(View v) {
switch(v.getId()) {
case R.id.btn_lang_en:
//save "en" to SharedPref here
break;
case R.id.btn_lang_kh:
//save "kh" to SharedPref here
break;
default:
break;
}
getActivity().recreate();
}
Dann erstellen Sie eine statische Methode, die ContextWrapper
zurückgibt, möglicherweise in einer Utils-Klasse (coz ist das, was ich getan habe, lul).
public static ContextWrapper changeLang(Context context, String lang_code){
Locale sysLocale;
Resources rs = context.getResources();
Configuration config = rs.getConfiguration();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
sysLocale = config.getLocales().get(0);
} else {
sysLocale = config.locale;
}
if (!lang_code.equals("") && !sysLocale.getLanguage().equals(lang_code)) {
Locale locale = new Locale(lang_code);
Locale.setDefault(locale);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
config.setLocale(locale);
} else {
config.locale = 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 ContextWrapper(context);
}
Laden Sie abschließend den Sprachcode von SharedPreferences
in der ALL ACTIVITY'S attachBaseContext(Context newBase)
-Methode.
@Override
protected void attachBaseContext(Context newBase) {
String lang_code = "en"; //load it from SharedPref
Context context = Utils.changeLang(newBase, lang_code);
super.attachBaseContext(context);
}
BONUS: Um Palm Sweat auf der Tastatur zu speichern, habe ich eine LangSupportBaseActivity
-Klasse erstellt, die die Activity
erweitert und den letzten Code-Code dort verwendet. Und ich habe alle anderen Aktivitäten erweitert LangSupportBaseActivity
.
Beispiel:
public class LangSupportBaseActivity extends Activity{
...blab blab blab so on and so forth lines of neccessary code
@Override
protected void attachBaseContext(Context newBase) {
String lang_code = "en"; //load it from SharedPref
Context context = Utils.changeLang(newBase, lang_code);
super.attachBaseContext(context);
}
}
public class HomeActivity extends LangSupportBaseActivity{
...blab blab blab
}
Die obigen Antworten brachten mich auf den richtigen Weg, ließen jedoch einige Probleme
Um das erste Element zu reparieren, habe ich beim Start der App das Standardgebietsschema gespeichert.
Note Wenn Ihre Standardsprache auf "en" gesetzt ist, müssen die Ländereinstellungen von "enGB" oder "enUS" beide dem Standardgebietsschema entsprechen (es sei denn, Sie geben getrennte Lokalisierungen für sie an). Ähnlich wie in dem folgenden Beispiel, wenn das Gebietsschema des Benutzers arLY (Arabisch-Libyen) ist, muss die DefLanguage "ar" und nicht "arLY" sein.
private Locale defLocale = Locale.getDefault();
private Locale locale = Locale.getDefault();
public static myApplication myApp;
public static Resources res;
private static String defLanguage = Locale.getDefault().getLanguage() + Locale.getDefault().getCountry();
private static sLanguage = "en";
private static final Set<String> SUPPORTEDLANGUAGES = new HashSet<>(Arrays.asList(new String[]{"en", "ar", "arEG"}));
@Override
protected void attachBaseContext(Context base) {
if (myApp == null) myApp = this;
if (base == null) super.attachBaseContext(this);
else super.attachBaseContext(setLocale(base));
}
@Override
public void onCreate() {
myApp = this;
if (!SUPPORTEDLANGUAGES.contains(test)) {
// The default locale (eg enUS) is not in the supported list - lets see if the language is
if (SUPPORTEDLANGUAGES.contains(defLanguage.substring(0,2))) {
defLanguage = defLanguage.substring(0,2);
}
}
}
private static void setLanguage(String sLang) {
Configuration baseCfg = myApp.getBaseContext().getResources().getConfiguration();
if ( sLang.length() > 2 ) {
String s[] = sLang.split("_");
myApp.locale = new Locale(s[0],s[1]);
sLanguage = s[0] + s[1];
}
else {
myApp.locale = new Locale(sLang);
sLanguage = sLang;
}
}
public static Context setLocale(Context ctx) {
Locale.setDefault(myApp.locale);
Resources tempRes = ctx.getResources();
Configuration config = tempRes.getConfiguration();
if (Build.VERSION.SDK_INT >= 24) {
// If changing to the app default language, set locale to the default locale
if (sLanguage.equals(myApp.defLanguage)) {
config.setLocale(myApp.defLocale);
// restored the default locale as well
Locale.setDefault(myApp.defLocale);
}
else config.setLocale(myApp.locale);
ctx = ctx.createConfigurationContext(config);
// update the resources object to point to the current localisation
res = ctx.getResources();
} else {
config.locale = myApp.locale;
tempRes.updateConfiguration(config, tempRes.getDisplayMetrics());
}
return ctx;
}
Um die RTL-Probleme zu beheben, erweiterte ich AppCompatActivity gemäß den Fragmenten-Kommentaren in dieser Antwort
public class myCompatActivity extends AppCompatActivity {
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(myApplication.setLocale(base));
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (Build.VERSION.SDK_INT >= 17) {
getWindow().getDecorView().setLayoutDirection(myApplication.isRTL() ?
View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
}
}
}