Ändern des Gebietsschemas in der App selbst

197

Meine Benutzer können das Gebietsschema in der App ändern (sie möchten möglicherweise ihre Telefoneinstellungen auf Englisch halten, aber den Inhalt meiner App auf Französisch, Niederländisch oder einer anderen Sprache lesen ...)

Warum funktioniert das in 1.5 / 1.6 einwandfrei, aber NICHT mehr in 2.0 ???

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    switch(item.getItemId()) {
    case 201:
        Locale locale2 = new Locale("fr"); 
        Locale.setDefault(locale2);
        Configuration config2 = new Configuration();
        config2.locale = locale2;
        getBaseContext().getResources().updateConfiguration(
            config2, getBaseContext().getResources().getDisplayMetrics());
        // loading data ...
        refresh();
        // refresh the tabs and their content
        refresh_Tab ();   
     break;
     case 201: etc...

Das Problem ist, dass das MENÜ jedes Mal mehr "schrumpft", wenn der Benutzer die obigen Codezeilen durchläuft ...

Dies ist das Menü, das verkleinert wird:

@Override
public boolean onCreateOptionsMenu(Menu menu) {
    menu.add(0, 100, 1, "REFRESH").setIcon(android.R.drawable.ic_menu_compass);
    SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
        langMenu.add(1, 201, 0, "Nederlands");
        langMenu.add(1, 202, 0, "Français");
    menu.add(0, 250, 4, R.string.OptionMenu2).setIcon(android.R.drawable.ic_menu_send);
    menu.add(0, 300, 5, R.string.OptionMenu3).setIcon(android.R.drawable.ic_menu_preferences);
    menu.add(0, 350, 3, R.string.OptionMenu4).setIcon(android.R.drawable.ic_menu_more);
    menu.add(0, 400, 6, "Exit").setIcon(android.R.drawable.ic_menu_delete);

    return super.onCreateOptionsMenu(menu);
}

Was muss ich in API Level 5 tun, damit dies wieder funktioniert?

HIER IST DER VOLLSTÄNDIGE CODE, WENN SIE DIESES TESTEN WOLLEN:

import java.util.Locale;

import android.app.Activity;
import android.content.res.Configuration;
import android.os.Bundle;
import android.view.Menu;
import android.view.MenuItem;
import android.view.SubMenu;
import android.widget.Toast;

public class Main extends Activity {
    /** Called when the activity is first created. */


    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {

        SubMenu langMenu = menu.addSubMenu(0, 200, 2, "NL-FR").setIcon(android.R.drawable.ic_menu_rotate);
            langMenu.add(1, 201, 0, "Nederlands");
            langMenu.add(1, 202, 0, "Français");

        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch(item.getItemId()){

        case 201:

            Locale locale = new Locale("nl"); 
            Locale.setDefault(locale);
            Configuration config = new Configuration();
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
            Toast.makeText(this, "Locale in Nederlands !", Toast.LENGTH_LONG).show();
            break;

        case 202:

            Locale locale2 = new Locale("fr"); 
            Locale.setDefault(locale2);
            Configuration config2 = new Configuration();
            config2.locale = locale2;
            getBaseContext().getResources().updateConfiguration(config2, getBaseContext().getResources().getDisplayMetrics());

            Toast.makeText(this, "Locale en Français !", Toast.LENGTH_LONG).show();
            break;  

        }
        return super.onOptionsItemSelected(item);
    }
}

UND HIER IST DAS MANIFEST:

<?xml version="1.0" encoding="utf-8"?>
    <manifest xmlns:android="http://schemas.android.com/apk/res/android"
          package="com.cousinHub.ChangeLocale"
          android:versionCode="1"
          android:versionName="1.0">
        <application android:icon="@drawable/icon" android:label="@string/app_name">
            <activity android:name=".Main"
                      android:label="@string/app_name">
                <intent-filter>
                    <action android:name="android.intent.action.MAIN" />
                    <category android:name="android.intent.category.LAUNCHER" />
                </intent-filter>
            </activity>
        </application>
        <uses-sdk android:minSdkVersion="3" /> 
    </manifest>

DAS HABE ICH GEFUNDEN:

<uses-sdk android:minSdkVersion="5" />

=> ES FUNKTIONIERT NUR FEIN ...

<uses-sdk android:minSdkVersion="3" />

=> Das Menü wird jedes Mal kleiner, wenn Sie das Gebietsschema ändern !!!

Was soll ich tun, wenn ich meine Anwendung für Benutzer unter 1.5 zugänglich halten möchte?

Hubert
quelle
Was meinst du mit "schrumpft"?
CommonsWare
es wird jedes Mal kleiner und kleiner. sollte ich vielleicht etwas anderes als getBaseContext verwenden?
Hubert
Vielleicht sind es nicht diese Zeilen, die das Problem verursachen, denn wenn ich eine sehr einfache App mache, die nur die Änderung im Gebietsschema vornimmt, habe ich nicht das gleiche "Verkleinern des Menüs". Ich dachte, es lag vielleicht daran, dass meine Aktivität tatsächlich eine TabActivity war, aber selbst damit kann ich das Problem nicht neu erstellen. Ich muss weiter untersuchen, was die genaue Ursache für diesen Fehler ist ... dann suchen Sie nicht weiter. Ich werde die Antwort hier posten, wenn ich sie finde. Cheers, H.
Hubert
Ich habe meinen ersten Beitrag bearbeitet und ein Beispiel für die Erstellung des Problems gegeben. Und ich habe tatsächlich bemerkt, dass beim Ändern der Zeile <used-sdk android: minSdkVersion = "5" /> auf "3" ... tatsächlich das Problem auftritt!
Hubert
1
Sie können die folgende Bibliothek verwenden, die die Sprachliste und die Einstellungen für Ihren Einstellungsbildschirm enthält und die Sprache in Ihrer Anwendung überschreibt: github.com/delight-im/Android-Languages
caw

Antworten:

151

Bei der ursprünglichen Frage geht es nicht genau um das Gebietsschema selbst. Alle anderen Fragen zum Gebietsschema beziehen sich auf dieses. Deshalb wollte ich das Thema hier klären. Ich habe diese Frage als Ausgangspunkt für meinen eigenen Gebietsschema-Umschaltcode verwendet und festgestellt, dass die Methode nicht genau korrekt ist. Dies funktioniert jedoch nur bis zu einer Konfigurationsänderung (z. B. Bildschirmdrehung) und nur in dieser bestimmten Aktivität. Wenn ich eine Weile mit einem Code spiele, bin ich zu folgendem Ansatz gekommen:

Ich habe android.app.Application erweitert und den folgenden Code hinzugefügt:

public class MyApplication extends Application
{
    private Locale locale = null;

    @Override
    public void onConfigurationChanged(Configuration newConfig)
    {
        super.onConfigurationChanged(newConfig);
        if (locale != null)
        {
            newConfig.locale = locale;
            Locale.setDefault(locale);
            getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
        }
    }

    @Override
    public void onCreate()
    {
        super.onCreate();

        SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);

        Configuration config = getBaseContext().getResources().getConfiguration();

        String lang = settings.getString(getString(R.string.pref_locale), "");
        if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang))
        {
            locale = new Locale(lang);
            Locale.setDefault(locale);
            config.locale = locale;
            getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
        }
    }
}

Dieser Code stellt sicher, dass für jede Aktivität ein benutzerdefiniertes Gebietsschema festgelegt ist und dass es bei Rotation und anderen Ereignissen nicht zurückgesetzt wird.

Ich habe auch viel Zeit damit verbracht, die Änderung der Einstellungen so anzuwenden, dass sie sofort angewendet wird, aber es gelang mir nicht: Die Sprache wurde beim Neustart der Aktivität korrekt geändert, aber Zahlenformate und andere Gebietsschemaeigenschaften wurden erst nach dem vollständigen Neustart der Anwendung angewendet.

Änderungen an AndroidManifest.xml

Vergessen Sie nicht android:configChanges="layoutDirection|locale", jeder Aktivität bei AndroidManifest sowie android:name=".MyApplication"dem <application>Element etwas hinzuzufügen .

Andrey Novikov
quelle
1
Wird Ihre bevorzugte Aktivität von Ihrer Hauptaktivität aufgerufen? Sie können dies in den On-Lebenslauf einfügen ... @Override protected void onResume () {if (! (PreferenceManager.getDefaultSharedPreferences (getApplicationContext ()). getString ("listLanguage", "en") .equals (langPreference)) { Aktualisierung(); } super.onResume (); } private void refresh () {finish (); Intent myIntent = new Intent (Main.this, Main.class); startActivity (myIntent); }
Trgraglia
1
Dies ist eigentlich das, was ich brauche. Früher habe ich updateConfiguration () in MainActivity aufgerufen, aber die Menüs werden nicht aktualisiert, wenn die Anwendung beendet und dann gestartet wird. Ihre Lösung ist richtig. Wenn Sie Zeichenfolgen sofort aktualisieren möchten, müssen Sie die Zeichenfolgen wahrscheinlich manuell zurücksetzen. (z. B. Tastenbeschriftung zurücksetzen oder Menü neu erstellen).
Smaragdhieu
Beeinträchtigt dies das Gebietsschema des Android-Systems?
Kostadin
17
Beachten Sie, dass Sie niemals ein Konfigurationsobjekt ändern dürfen, das Ihnen Android ( configund newConfig) zur Verfügung stellt. Sie sollten zuerst eine Kopie mit config = new Configuration(config);erstellen. Ich habe gerade eine Stunde damit verbracht, diesen Code zu debuggen, bevor ich das Problem herausgefunden habe (es hat dazu geführt, dass die Aktivität endlos geflasht hat).
imgx64
1
Diese Technik weist unter Android 7+ Probleme auf, wenn die App von einem Kaltstart aus gestartet wird, wobei die Systemschriftgröße auf groß eingestellt ist. Google hat die Funktionsweise geändert updateConfiguration(), sodass Ihre Aktivität manchmal in zwei verschiedenen Sprachen angezeigt wird (insbesondere in Dialogen). Weitere Informationen: stackoverflow.com/questions/39705739/…
Mr-IDE
61

Nach einer erholsamen Nacht fand ich die Antwort im Web (eine einfache Google-Suche in der folgenden Zeile " getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), hier ist sie:

Linktext => Dieser Link zeigt auch, screenshotswas passiert!

Dichte war hier das Problem , ich musste dies in der AndroidManifest.xml haben

<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true"
/>

Das wichtigste ist das Android: anyDensity = "true" .

Vergessen Sie nicht, AndroidManifest.xmlfür jede Aktivität Folgendes hinzuzufügen: (für Android 4.1 und niedriger):

android:configChanges="locale"

Diese Version wird benötigt, wenn Sie hier eine Erklärung für Android 4.2 (API Level 17) erstellen :

android:configChanges="locale|layoutDirection"
Hubert
quelle
3
Ich hatte android: anyDensity = "false" aufgrund des Ratschlags von Google (Strategien für Legacy-Anwendungen - Punkt 9: developer.android.com/intl/fr/guide/practices/… ). Sei dann vorsichtig!
Hubert
2
Die Texte auf den Schaltflächen ändern sich nicht, nachdem ich das Gebietsschema geändert habe. Ich frage mich, ob es eine Lösung gibt, um alle Widgets zu aktualisieren, die Zeichenfolgenressourcen enthalten. Jetzt zeichne ich den Text mit setText (getString (R.string.button_label)) in OnRestart () neu. (Natürlich ändert es sich nach dem Neustart der Anwendung.)
Smaragdhieu
Sie können Recreate-Aktivität aufrufenactivity.recreate()
To Kra
59

In Android M funktioniert die Top-Lösung nicht. Ich habe eine Hilfsklasse geschrieben, um das zu beheben, was Sie von Ihrer Anwendungsklasse und allen Aktivitäten aufrufen sollten (ich würde vorschlagen, eine BaseActivity zu erstellen und dann alle Aktivitäten von dieser zu erben.

Hinweis : Dies unterstützt auch die richtige RTL-Layoutrichtung.

Hilfsklasse:

public class LocaleUtils {

    private static Locale sLocale;

    public static void setLocale(Locale locale) {
        sLocale = locale;
        if(sLocale != null) {
            Locale.setDefault(sLocale);
        }
    }

    public static void updateConfig(ContextThemeWrapper wrapper) {
        if(sLocale != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
            Configuration configuration = new Configuration();
            configuration.setLocale(sLocale);
            wrapper.applyOverrideConfiguration(configuration);
        }
    }

    public static void updateConfig(Application app, Configuration configuration) {
        if (sLocale != null && Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            //Wrapping the configuration to avoid Activity endless loop
            Configuration config = new Configuration(configuration);
            // We must use the now-deprecated config.locale and res.updateConfiguration here,
            // because the replacements aren't available till API level 24 and 17 respectively.
            config.locale = sLocale;
            Resources res = app.getBaseContext().getResources();
            res.updateConfiguration(config, res.getDisplayMetrics());
        }
    }
}

Anwendung:

public class App extends Application {
    public void onCreate(){
        super.onCreate();

        LocaleUtils.setLocale(new Locale("iw"));
        LocaleUtils.updateConfig(this, getBaseContext().getResources().getConfiguration());
    }

    @Override
    public void onConfigurationChanged(Configuration newConfig) {
        super.onConfigurationChanged(newConfig);
        LocaleUtils.updateConfig(this, newConfig);
    }
}

BaseActivity:

public class BaseActivity extends Activity {
    public BaseActivity() {
        LocaleUtils.updateConfig(this);
    }
}
Roberto B.
quelle
11
Dies sollte als Antwort akzeptiert werden. Stellen Sie sicher, dass updateConfig vom Konstruktor aufgerufen wird, nicht von onCreate der Aktivitätsklasse. Ich habe diesen Fehler gemacht.
Hitesh Sahu
1
Dies hängt davon ab, wie Sie dies in Ihrem Programm implementiert haben. Wenn Sie onConfigurationChanged () überschrieben haben, müssen Sie .updateConfig () nicht aufrufen, und deshalb wird der Fehler angezeigt, da er zweimal aufgerufen wird. Wenn Sie sagen, dass nur das Aufrufen von .setLocale () nicht funktioniert, liegt dies daran, dass Sie Ihre Benutzeroberfläche aktualisieren müssen, indem Sie entweder recreate () für die Aktivität oder Ihre eigene Methode in Aktivitäten / Fragmenten aufrufen (ich bevorzuge die erstere, auch wenn es ist nicht glatt, aber Sie werden sich nie um Probleme sorgen)
RJFares
3
// android.support.v7.view.ContextThemeWrapper importieren; import android.view.ContextThemeWrapper; // korrekter Namespace
Mehdi Rostami
1
Sollte der BaseActivity()Konstruktor nicht aufrufen super()?
LarsH
1
Umfasst diese Lösung implizit das Einfügen <application android:name=".App">des Manifests? Wie wäre es, android:configChanges="layoutDirection|locale"zu jeder Aktivität im Manifest etwas hinzuzufügen ?
LarsH
10

Dies ist für meinen Kommentar zu Andreys Antwort, aber ich kann keinen Code in die Kommentare aufnehmen.

Wird Ihre bevorzugte Aktivität von Ihrer Hauptaktivität aufgerufen? Sie könnten dies in den Lebenslauf setzen ...

@Override
protected void onResume() {
    if (!(PreferenceManager.getDefaultSharedPreferences(
            getApplicationContext()).getString("listLanguage", "en")
            .equals(langPreference))) {
        refresh();
    }
    super.onResume();
}

private void refresh() {
    finish();
    Intent myIntent = new Intent(Main.this, Main.class);
    startActivity(myIntent);
}
trgraglia
quelle
2
Sie meinen, wenn ich die Sprache zur Laufzeit ändern möchte, muss ich diese Aktivität beenden und diese Aktivität erneut starten, um Änderungen zu erhalten? Gibt es eine andere Möglichkeit, dies zu ändern, da es nicht gut ist, die Aktivität jedes Mal zu beenden und erneut zu starten?
Shreyash Mahajan
1
Besser machenactivity.recreate()
Zu Kra
@trgradlia, ich möchte den obigen Code verwenden. Bitte sagen Sie mir, was ist langPreference? Ich denke, wir vergleichen kürzlich geänderte Sprache und vorherige Sprache.
Mahen
1
Aktivität hat recreate()Methode, die Sie recreate()anstelle von refresh()in Ihrem Code verwenden. Dies wird Ihre 3 Codezeilen in refresh()Methode
speichern
1
@InzimamTariqIT, Danke für den Tipp ... Die Antwort ist fast 7 Jahre alt, also werde ich sie so lassen wie sie ist und die Leute können Ihre Verbesserung hier in den Kommentaren finden.
Trgraglia
6

Ich konnte android: anyDensity = "true" nicht verwenden, da Objekte in meinem Spiel völlig anders positioniert wären ... anscheinend ist dies auch der Trick:

// Gebietsschema erstellen
Gebietsschema locale2 = neues Gebietsschema (loc); 
Locale.setDefault (locale2);
Konfiguration config2 = neue Konfiguration ();
config2.locale = locale2;

// Gebietsschema aktualisieren
mContext.getResources (). updateConfiguration (config2, null);
nikib3ro
quelle
Diese Lösung ist am besten für mich gerade verwendetcode Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
Huds0nHawk
1

Wenn Sie die Menüoptionen zum sofortigen Ändern des Gebietsschemas beeinflussen möchten, müssen Sie dies tun.

//onCreate method calls only once when menu is called first time.
public boolean onCreateOptionsMenu(Menu menu) {
    super.onCreateOptionsMenu(menu);
    //1.Here you can add your  locale settings . 
    //2.Your menu declaration.
}
//This method is called when your menu is opend to again....
@Override
public boolean onMenuOpened(int featureId, Menu menu) {
    menu.clear();
    onCreateOptionsMenu(menu);
    return super.onMenuOpened(featureId, menu);
}
Ramesh Akula
quelle
Aber löscht diese Lösung nicht das Menü bei jedem geöffneten Menü? Ich nehme an, onMenuOpenedsollte nach dem Gebietsschemawechsel nur einmal aufgerufen werden.
Trante