【问题标题】:Changing Locale within the app itself在应用程序本身内更改区域设置
【发布时间】:2011-01-16 22:01:05
【问题描述】:

我的用户可以更改应用程序中的区域设置(他们可能希望保持他们的手机设置为英语,但阅读我的应用程序内容为法语、荷兰语或任何其他语言...)

为什么这在 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);
}

我应该在 API 级别 5 中做些什么才能让这项工作再次发挥作用?

如果你想测试,这里是完整的代码:

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 上的用户可以访问我的应用程序,我应该怎么做??

【问题讨论】:

  • “收缩”是什么意思?
  • 它每次都变得越来越小。我应该使用 getBaseContext 以外的其他东西吗?
  • 也许不是这些行造成了问题,因为当我做一个非常基本的应用程序只更改区域设置时,我没有相同的“菜单缩小”。我认为这可能是因为我的 Activity 实际上是 TabActivity,但即使这样,我也无法重新创建问题。我将不得不进一步调查这个错误的确切原因......然后不要进一步搜索。当我找到答案时,我会在这里发布答案。干杯,H。
  • 我编辑了我的第一篇文章,举例说明了如何创建问题。事实上,我注意到,当我将 更改为“3”时......确实出现了问题!
  • 您可以使用以下库,它提供语言列表、设置屏幕的首选项并覆盖应用程序中的语言:github.com/delight-im/Android-Languages

标签: android menu locale


【解决方案1】:

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

基础活动:

public class BaseActivity extends Activity {
    public BaseActivity() {
        LocaleUtils.updateConfig(this);
    }
}

【讨论】:

  • 这应该是被接受的答案。确保 updateConfig 应该从构造函数调用而不是从活动类的 onCreate 调用,我犯了这个错误..
  • 这取决于你如何在你的程序中实现它。如果您已覆盖 onConfigurationChanged() 您不需要调用 .updateConfig() 这就是您收到错误的原因,因为它被调用了两次。如果您说仅调用 .setLocale() 不起作用,这是因为您需要通过在活动上调用 recreate() 或在活动/片段中调用您自己的方法来刷新 UI(即使我更喜欢前者,即使它并不顺利,但你永远不会担心问题)
  • //import android.support.v7.view.ContextThemeWrapper; import android.view.ContextThemeWrapper;//正确的命名空间
  • 不应该BaseActivity()构造函数调用super()吗?
  • 此解决方案是否隐含地将&lt;application android:name=".App"&gt; 放入清单中?将android:configChanges="layoutDirection|locale" 添加到清单中的每个活动怎么样?
【解决方案2】:

虽然最初的问题并不完全与语言环境本身有关,但所有其他与语言环境相关的问题都参考了这个问题。这就是为什么我想在这里澄清这个问题。我使用这个问题作为我自己的语言环境切换代码的​​起点,并发现该方法并不完全正确。它有效,但仅在任何配置更改(例如屏幕旋转)之前,并且仅在该特定 Activity 中有效。玩了一段时间的代码,我最终采用了以下方法:

我已经扩展了 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());
        }
    }
}

此代码确保每个 Activity 都设置了自定义区域设置,并且不会在轮换和其他事件时重置。

我也花了很多时间试图使首选项更改立即应用但没有成功:在 Activity 重新启动时语言更改正确,但数字格式和其他区域设置属性直到应用程序完全重新启动后才应用。

更改为AndroidManifest.xml

不要忘记将android:configChanges="layoutDirection|locale" 添加到AndroidManifest 中的每个活动,以及将android:name=".MyApplication" 添加到&lt;application&gt; 元素。

【讨论】:

  • 您的偏好活动是否从您的主要活动中调用?你可以把它放在简历中... @Override protected void onResume() { if (!(PreferenceManager.getDefaultSharedPreferences( getApplicationContext()).getString("listLanguage", "en") .equals(langPreference))) {刷新(); } super.onResume(); } 私人无效刷新() { 完成();意图 myIntent = new Intent(Main.this, Main.class);开始活动(我的意图); }
  • 这实际上是我需要的,我曾经在 MainActivity 中调用 updateConfiguration() 但是当应用程序终止然后启动时菜单不会更新。你的解决方案是对的。如果您想立即刷新字符串,我认为您仍然必须手动重置字符串。 (例如重置按钮标签或重新创建菜单)。
  • 这会干扰 Android 系统语言环境吗?
  • 请注意,您必须永远修改 Android 提供给您的配置对象(confignewConfig)。你应该先用config = new Configuration(config); 复制一份。在发现问题之前我只花了一个小时调试这段代码(它导致活动无休止地闪烁)。
  • 此技术在 Android 7+ 上从冷启动应用程序时出现故障,系统字体大小设置为大。 Google 改变了updateConfiguration() 的工作方式,因此有时您的活动会以两种不同的语言显示(尤其是在对话框中)。更多信息:stackoverflow.com/questions/39705739/…
【解决方案3】:

如果您想影响菜单选项以立即更改区域设置。您必须这样做。

//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 应该只调用一次,在更改语言环境之后。
【解决方案4】:

睡了一夜之后,我在网上找到了答案(在下面一行“getBaseContext().getResources().updateConfiguration(mConfig, getBaseContext().getResources().getDisplayMetrics());”上简单的谷歌搜索),这里是:

link text => 这个链接还显示了screenshots 正在发生的事情!

密度是这里的问题,我需要在 AndroidManifest.xml 中有这个

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

最重要的是android:anyDensity="true"

不要忘记在AndroidManifest.xml 中为每个活动添加以下内容(适用于 Android 4.1 及更低版本):

android:configChanges="locale"

为 Android 4.2(API 级别 17)构建时需要此版本explanation here

android:configChanges="locale|layoutDirection"

【讨论】:

  • 我有 android:anyDensity="false" 由于 Google 的建议(旧应用程序策略 - 第 9 点:developer.android.com/intl/fr/guide/practices/…)。那就小心点!
  • 更改语言环境后,按钮上的文本不会更改。我想知道是否有任何解决方案来刷新所有包含字符串资源的小部件。现在我在 OnRestart() 中使用 setText(getString(R.string.button_label)) 重绘文本。 (当然重启应用后会发生变化。)
  • 你可以调用recreate activityactivity.recreate()
【解决方案5】:

这是我对 Andrey 的回答的评论,但我不能在 cmets 中包含代码。

是否从主要活动中调用了您的偏好活动?您可以将其放在简历中...

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

【讨论】:

  • 您的意思是,如果我想在运行时更改语言,那么我必须完成该活动并再次启动该活动以获取更改?有没有其他方法可以改变它,因为每次完成活动并重新开始都是不好的。
  • 最好activity.recreate()
  • @trgradlia ,我想使用上面的代码。请告诉我,什么是 langPreference ?我认为我们正在比较最近更改的语言和以前的语言。
  • Activity 有recreate() 方法,所以在你的代码中使用recreate() 代替refresh()。这会将您的 3 行代码保存在 refresh()method
  • @InzimamTariqIT,感谢您的提示...答案已经有将近 7 年的历史了,所以我将保持原样,人们可以在 cmets 中找到您的改进。
【解决方案6】:

我不能使用 android:anyDensity="true" 因为我的游戏中的对象的位置会完全不同......似乎这也可以解决问题:

// 创建语言环境 区域设置 locale2 = 新区域设置 (loc); Locale.setDefault(locale2); 配置 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); }
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2023-04-02
  • 2013-01-01
  • 1970-01-01
  • 2019-03-22
  • 1970-01-01
  • 2011-12-24
  • 2013-04-24
相关资源
最近更新 更多