Зміна місцеположення в самому додатку


197

Мої користувачі можуть змінити Локальний вміст у програмі (вони можуть зберігати налаштування телефону англійською мовою, але читати вміст мого додатка французькою, голландською або будь-якою іншою мовою ...)

Чому це працює прекрасно в 1.5 / 1.6, але НЕ в 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...

Проблема полягає в тому, що МЕНЮ "стискається" все більше і більше кожного разу, коли користувач переходить рядки коду вище ...

Це меню, яке скорочується:

@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);
}

Що мені робити на рівні 5 API, щоб зробити цю роботу знову?

ТУТ ПОВНИЙ КОД, ЯКЩО Ви хочете випробувати це:

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);
    }
}

І ТУТ МІНЕСТЬ:

<?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>

ЦЕ Я ЗНАЙДАЮ:

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

=> ЦІ РОБОТИ ДУЖЕ ТОПЛО ...

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

=> Меню скорочується щоразу, коли ви змінюєте локаль !!!

так як я хочу тримати свою програму доступною для користувачів на 1.5, що мені робити ??


Що ви маєте на увазі під «скороченнями»?
CommonsWare

вона стає щораз меншою і меншою. Чи можу я використовувати щось інше, ніж getBaseContext?
Губерт

Можливо, не ці рядки створюють проблему, тому що коли я роблю дуже базовий додаток, що робить лише зміну в Locale, у мене немає такого ж «скорочення меню». Я подумав, що це може бути тому, що моя активність насправді була TabActivity, але навіть з цим я не можу створити проблему. Мені доведеться далі дослідити, яка саме причина цієї помилки ... не шукайте далі. Відповідь я опублікую тут, коли знайду. Ура, Х.
Губерт

Я редагував своє перше повідомлення, даючи приклад, як створити проблему. І я помітив насправді, що коли я змінюю рядок <use-sdk android: minSdkVersion = "5" /> на "3" ... справді проблема з'являється!
Губерт

1
Ви можете використовувати таку бібліотеку, яка містить список мов, уподобання екрана налаштувань і замінює мову у вашій програмі: github.com/delight-im/Android-Languages
caw

Відповіді:


151

Оскільки в оригінальному питанні не йдеться саме про локаль, всі інші питання, пов’язані з мовою, посилаються на це питання. Тому я хотів уточнити тут питання. Я використав це питання як вихідну точку для власного коду перемикання локалі та виявив, що метод не зовсім правильний. Він працює, але лише до зміни будь-якої конфігурації (наприклад, обертання екрана) і лише в цій конкретній діяльності. Граючи з кодом деякий час, я закінчився таким підходом:

Я розширив android.app.Application і додав наступний код:

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());
        }
    }
}

Цей код гарантує, що для кожної діяльності буде встановлено спеціальний локальний набір, і він не буде скинутись під час обертання та інших подій.

Я також витратив чимало часу, намагаючись зробити так, щоб зміни уподобань були застосовані негайно, але не вдалося: мова переросла коректно під час перезавантаження діяльності, але формати чисел та інші властивості локалі не застосовувалися до повного перезапуску програми.

Зміни в AndroidManifest.xml

Не забудьте додати android:configChanges="layoutDirection|locale"до кожної діяльності на AndroidManifest, а також android:name=".MyApplication"до <application>елемента.


1
Чи вважаєте, що ви віддаєте перевагу діяльності, яку вам називають основною діяльністю? Ви можете розмістити це в резюме ... @ Захищена недійсна порожнеча onResume () {if (! (PreferenceManager.getDefaultSharedPreferences (getApplicationContext ()). getString ("listLanguage", "en") .equals (langPreference))) { refresh (); } super.onResume (); } private void refresh () {закінчити (); Intent myIntent = новий намір (Main.this, Main.class); startActivity (myIntent); }
trgraglia

1
Це власне те, що мені потрібно, я закликав updateConfiguration () в MainActivity, але меню не оновлюється, коли додаток завершується, а потім запускається. Ви рішення правильне. Якщо ви хочете негайно оновити рядки, я думаю, ви все одно повинні скинути рядки вручну. (наприклад, мітка кнопки скидання або меню відтворення).
смарагдовий

Чи це заважає локальній системі Android?
Костадин

17
Зауважте, що ви ніколи не повинні змінювати об’єкт конфігурації, який дає вам ( configі newConfig) Android . config = new Configuration(config);Спершу слід зробити копію . Я щойно витратив годину налагодження цього коду, перш ніж з'ясувати проблему (це спричиняло активність нескінченно спалахує).
imgx64

1
Ця технологія має збої на Android 7+ під час запуску програми з холодного старту, розмір шрифту системи встановлений на великий. Google змінив спосіб updateConfiguration()роботи, тому іноді ваша активність відображатиметься двома різними мовами (особливо в діалогах). Більше інформації: stackoverflow.com/questions/39705739/…
Mr-IDE

61

Після доброї ночі сну я знайшов відповідь в Інтернеті (простий пошук Google у наступному рядку " getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());"), ось він:

текст посилання => це посилання також показує, screenshotsщо відбувається!

Тут була проблема густини , мені потрібно було це мати в AndroidManifest.xml

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

Найважливіший андроїд: anyDensity = "true" .

Не забудьте додати наступне AndroidManifest.xmlдля кожної діяльності (для Android 4.1 і нижче):

android:configChanges="locale"

Ця версія потрібна при створенні пояснення для Android 4.2 (рівень API 17) тут :

android:configChanges="locale|layoutDirection"

3
У мене був android: anyDensity = "false" завдяки порадам Google (Стратегії для Legacy Applications - пункт 9: developer.android.com/intl/fr/guide/practices/… ). Тоді будьте обережні!
Губерт

2
Тексти на кнопках не змінюються після того, як я зміню локальний. Цікаво, чи є рішення, щоб оновити всі віджети, які містять рядкові ресурси. Тепер я перемальовую текст, використовуючи setText (getString (R.string.button_label)) у OnRestart (). (Звичайно, це змінюється після перезавантаження програми.)
emeraldhieu

ви можете зателефонувати у відтворення діяльностіactivity.recreate()
до

59

В Android M топ-рішення не працюватиме. Я написав допоміжний клас, щоб виправити те, до чого слід зателефонувати з класу Application та всіх видів діяльності (я б запропонував створити BaseActivity, а потім зробити всі дії успадкованими від нього.

Примітка . Це також буде правильно підтримувати напрямок макета RTL.

Клас помічника:

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());
        }
    }
}

Застосування:

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);
    }
}

11
На це слід прийняти відповідь. Переконайтесь, що updateConfig повинен викликати від конструктора не з onCreate клас діяльності, я робив цю помилку ..
Hitesh Sahu

1
це залежить від того, як ти це реалізував у своїй програмі. Якщо ви перекрили onConfigurationChanged (), вам не потрібно викликати .updateConfig (), і саме тому ви отримуєте помилку, оскільки вона викликається двічі. якщо ви говорите про те, що лише виклик .setLocale () не працює, це тому, що вам потрібно оновити інтерфейс користувача, або зателефонувавши recreate () про активність, або власний метод у діяльності / фрагментах (я віддаю перевагу колишньому, навіть якщо це не гладко, але ви ніколи не будете турбуватися про проблеми)
RJFares

3
// імпортувати android.support.v7.view.ContextThemeWrapper; import android.view.ContextThemeWrapper; // правильний простір імен
Мехді Ростамі

1
Чи не повинен BaseActivity()конструктор дзвонити super()?
LarsH

1
Чи це рішення неявно включає включення <application android:name=".App">маніфесту? Як щодо додавання android:configChanges="layoutDirection|locale"до кожної діяльності в маніфесті?
LarsH

10

Це для мого коментаря до відповіді Андрія, але я не можу включити код у коментарі.

Чи вважаєте, що ви віддаєте перевагу діяльності, яку вам називають основною діяльністю? Ви можете розмістити це в резюме ...

@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);
}

2
Ви маєте на увазі, якщо я хочу змінити мову під час виконання, тоді мені доведеться закінчити цю діяльність і запустити цю активність знову, щоб отримати зміни? чи є якийсь інший спосіб змусити його змінити, оскільки це не добре щоразу закінчувати діяльність і починати її заново.
Шреяш Махаджан

1
краще зробитиactivity.recreate()
До

@trgradlia, я хочу використовувати вищевказаний код. Скажіть, будь ласка, що таке langPreference? Думаю, ми порівнюємо нещодавно змінений lang та попередній lang.
Mahen

1
Діяльність має recreate()метод, тому використовуйте recreate()замість refresh()свого коду. Це дозволить зберегти ваш 3 рядок коду в refresh()методі
Inzimam Tariq IT

1
@InzimamTariqIT, Дякую за пораду ... Відповіді майже 7 років, тому я залишу її такою, яка є, і люди зможуть знайти ваше покращення тут у коментарях.
trgraglia

6

Я не міг використати android: anyDensity = "true", оскільки об'єкти в моїй грі були б розміщені зовсім інакше ... схоже, це також робить трюк:

// створення локалу
Locale locale2 = новий Locale (loc); 
Locale.setDefault (locale2);
Configuration config2 = нова Конфігурація ();
config2.locale = locale2;

// оновлення місцевості
mContext.getResources (). updateConfiguration (config2, null);

Це рішення найкраще для мене щойно використав цеcode Locale.setDefault(mLocale); Configuration config = getBaseContext().getResources().getConfiguration(); if (!config.locale.equals(mLocale)) { config.locale = mLocale; getBaseContext().getResources().updateConfiguration(config, null); }
Huds0nHawk

1

Якщо ви хочете негайно вплинути на параметри меню, щоб змінити локаль. Вам потрібно зробити це так.

//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);
}

Але чи це рішення не очищає меню в кожному відкритому меню? Я припускаю, що onMenuOpenedслід викликати лише один раз, після зміни місцевості.
trante
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.