【问题标题】:How to refresh activity after changing language (Locale) inside application如何在应用程序内更改语言(区域设置)后刷新活动
【发布时间】:2011-12-24 08:29:24
【问题描述】:

我的应用程序用户可以从应用程序的设置中更改语言。是否可以在不影响一般语言设置的情况下更改应用程序内的语言? This question of stackoverflow 对我非常有用,我已经尝试过了。更改语言后,新创建的活动显示更改后的新语言,但当前活动和先前创建的处于暂停状态的活动不会更新。如何更新活动? 我也花了很多时间试图使偏好更改立即应用但没有成功。当应用程序重新启动时,所有活动都会重新创建,因此现在语言已正确更改。

android:configChanges="locale" 

还添加到所有活动的清单中。并且还支持全屏。 目前我还没有在活动的 onResume() 方法中做任何事情。 有没有办法刷新或更新活动(没有完成并重新开始)?我在 onResume() 方法中错过了一些事情吗?

【问题讨论】:

标签: android


【解决方案1】:

更改语言后新创建的活动显示更改为新的语言,但当前活动和之前创建的处于暂停状态的活动没有更新。如何更新活动?

在 API 11 (Honeycomb) 之前,使现有活动以新语言显示的最简单方法是重新启动它。这样你就不用费心自己重新加载每个资源了。

private void restartActivity() {
    Intent intent = getIntent();
    finish();
    startActivity(intent);
}

注册OnSharedPreferenceChangeListener,在其onShredPreferenceChanged() 中,如果语言首选项发生更改,则调用restartActivity()。在我的示例中,仅重新启动了 PreferenceActivity,但您应该能够通过设置标志来重新启动活动恢复时的其他活动。

更新(感谢@stackunderflow):从 API 11(Honeycomb)开始,您应该使用 recreate() 而不是 restartActivity()

public class PreferenceActivity extends android.preference.PreferenceActivity implements
        OnSharedPreferenceChangeListener {

    // ...

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals("pref_language")) {
            ((Application) getApplication()).setLocale();
            restartActivity();
        }
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addPreferencesFromResource(R.xml.preferences);
        getPreferenceScreen().getSharedPreferences().registerOnSharedPreferenceChangeListener(this);
    }

    @Override
    protected void onStop() {
        super.onStop();
        getPreferenceScreen().getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this);
    }
}

我有一个关于这个主题的博客post 更详细,但它是中文的。完整源码在github上:PreferenceActivity.java

【讨论】:

  • 我把它和 overridePendingTransition(0,0);丑陋,但它有效,看起来还不错=)
  • 从 API 11 (Honeycomb) 开始,您应该使用 recreate() 而不是 restartActivity 方法。
  • @stackunderflow 谢谢。与自己完成并重新启动活动不同,recreate() 不会破坏您保留的无头碎片。哈利路亚!
  • 因为我每次都使用微调器,所以它会无限完成并重新启动我的活动。
  • 不工作实际((应用程序)getApplication()).setLocale();
【解决方案2】:

如果我想象您在 manifest.xml 中设置 android:configChanges 并为多种语言创建多个目录,例如:values-fr OR values-nl,我可以建议此代码(在 Activity 类中):

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

    Button btn = (Button) findViewById(R.id.btn);
    btn.setOnClickListener(new OnClickListener() {

        @Override
        public void onClick(View v) {
            // change language by onclick a button
             Configuration newConfig = new Configuration();
             newConfig.locale = Locale.FRENCH;
             onConfigurationChanged(newConfig);
        }
    });
}

@Override
public void onConfigurationChanged(Configuration newConfig) {
    super.onConfigurationChanged(newConfig);

    getBaseContext().getResources().updateConfiguration(newConfig, getBaseContext().getResources().getDisplayMetrics());
    setContentView(R.layout.main);
    setTitle(R.string.app_name);

    // Checks the active language
    if (newConfig.locale == Locale.ENGLISH) {
        Toast.makeText(this, "English", Toast.LENGTH_SHORT).show();
    } else if (newConfig.locale == Locale.FRENCH){
        Toast.makeText(this, "French", Toast.LENGTH_SHORT).show();
    }
}

我测试了这段代码,是正确的。

【讨论】:

  • 此解决方案在 2.2 中不起作用对吧?您使用的是哪个版本的 sdk?
  • 我用过 2.2 和 4.0。我没有任何问题!
  • 如果是ListActivity呢?
  • @benleung 不一样,ListActivty 扩展了 Activity 并且有 onConfigurationChanged 方法
  • @benleung 我不知道你的问题到底是什么。如果你想了解 ListActivity,你应该问另一个问题。如果你想知道 ListActivty 可以有一个自定义布局,我告诉你是的,你可以。检查我放的上面的链接。
【解决方案3】:

由于已经为现有语言环境加载了字符串资源,因此已经打开的活动将不会使用来自新语言环境的字符串自动显示。解决此问题的唯一方法是重新加载所有字符串并在视图上再次设置它们。通常,对setContentView(...) 的调用将能够覆盖这一点(取决于您的 Activity 结构),但当然它具有丢失您拥有的任何视图状态的副作用。

public void onResume() {
    super.onResume();
    ...
    if (localeHasChanged) {
        setContentView(R.layout.xxx);
    }
    ...
}

您可能不想在onResume() 中每次都重新加载视图,但只有在语言环境发生更改时才需要。检查何时更新视图(即localeHasChanged)是将区域设置更改事件传播到先前活动的问题。这可以通过多种方式完成,例如使用静态单例状态或将此事件持久化到存储中。

您还可以尝试在可以更改语言环境时尽量减少可以打开的活动数量,例如通过让选择位于初始屏幕之一。

【讨论】:

  • 此解决方案避免重新加载活动(即完成和开始活动),如 aleung 上面给出的。谢谢@antonyt
【解决方案4】:

对于 Android 4.2 (API 17),您需要在 AndroidManifest.xml 中使用 android:configChanges="locale|layoutDirection"。见onConfigurationchanged is not called over jellybean(4.2.1)

【讨论】:

    【解决方案5】:

    您可以在语言更改时使用recreate(); 重新开始您的活动。

    我正在使用以下代码在语言更改时重新启动活动:

    SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(this);
    Configuration config = getBaseContext().getResources().getConfiguration();
    
    String lang = settings.getString("lang_list", "");
    
    if (! "".equals(lang) && ! config.locale.getLanguage().equals(lang)) {
          recreate();  //this is used for recreate activity
          Locale locale = new Locale(lang);
          Locale.setDefault(locale);
          config.locale = locale;
          getBaseContext().getResources().updateConfiguration(config, getBaseContext().getResources().getDisplayMetrics());
    }
    

    【讨论】:

    • 这段代码为什么会消失?它不在Application 的子类中。
    【解决方案6】:

    我们使用广播的方式是:

    1. 每次用户更改语言时发送广播
    2. AppActivity.onCreate() 中注册广播接收器并在AppActivity.onDestroy() 中取消注册
    3. BroadcastReceiver.onReceive() 中重新启动活动。

    AppActivity 是所有其他活动的子类的父活动。


    以下是我的代码中的 sn-p,未在项目之外进行测试,但应该会给您一个不错的想法。

    当用户改变语言时

    sendBroadcast(new Intent("Language.changed"));
    

    在父活动中

    public class AppActivity extends Activity {
    
        /**
         * The receiver that will handle the change of the language.
         */
        private BroadcastReceiver mLangaugeChangedReceiver;
    
        @Override
        protected void onCreate(final Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
    
            // ...
            // Other code here
            // ...
    
            // Define receiver
            mLangaugeChangedReceiver = new BroadcastReceiver() {
    
                @Override
                public void onReceive(final Context context, final Intent intent) {
                    startActivity(getIntent());
                    finish();
                }
            };
    
            // Register receiver
            registerReceiver(mLangaugeChangedReceiver, new IntentFilter("Language.changed"));
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
    
            // ...
            // Other cleanup code here
            // ...
    
            // Unregister receiver
            if (mLangaugeChangedReceiver != null) {
                try {
                    unregisterReceiver(mLangaugeChangedReceiver);
                    mLangaugeChangedReceiver = null;
                } catch (final Exception e) {}
            }
        }
    }
    

    这也将刷新更改语言的活动(如果它是上述活动的子类)。

    这会使您丢失任何数据,但如果这很重要,您应该已经使用 Actvity.onSaveInstanceState()Actvity.onRestoreInstanceState()(或类似名称)处理了这个问题。

    让我知道你对此的看法。

    干杯!

    【讨论】:

    • sendBroadcast 方法的参考是什么?
    【解决方案7】:

    逐步方法完整方法适用于所有 API 级别的设备。

    使用此方法,无需在屏幕上重新创建现有活动即可工作

    1. 为 attachBaseContext 使用 Base Activity 来设置区域语言并将此 Activity 扩展到所有 Activity

      open class  BaseAppCompactActivity() : AppCompatActivity() {
        override fun attachBaseContext(newBase: Context) {
            super.attachBaseContext(LocaleHelper.onAttach(newBase))    
        }
      }
      
    2. 使用 Application attachBaseContext 和 onConfigurationChanged 设置语言环境

      public class MyApplication extends Application {
      
        /**
         * overide to change local sothat language can be chnaged from android device  nogaut and above
         */
        @Override
        protected void attachBaseContext(Context base) {
            super.attachBaseContext(LocaleHelper.INSTANCE.onAttach(base));
        }
      
        @Override
        public void onConfigurationChanged(Configuration newConfig) {
            setLanguageFromNewConfig(newConfig);
            super.onConfigurationChanged(newConfig);
        }
      
        /*** also handle chnage  language if  device language chnaged **/
        private void setLanguageFromNewConfig(Configuration newConfig){
            Prefs.putSaveLocaleLanguage(this,  selectedLocaleLanguage );
            LocaleHelper.INSTANCE.onAttach(this);
        }
      }
      
    3. 使用 Locale Helper 处理语言更改,此方法适用于所有设备

      object LocaleHelper {
        private var defaultLanguage  :String = KycUtility.KYC_LANGUAGE.ENGLISH.languageCode
      
        fun onAttach(context: Context, defaultLanguage: String): Context {
            return setLocale(context, defaultLanguage)
        }
      
      
      
        fun setLocale(context: Context, language: String): Context {
            return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                updateResources(context, language)
            } else updateResourcesLegacy(context, language)
      
        }
      
      
        @TargetApi(Build.VERSION_CODES.N)
        private fun updateResources(context: Context, language: String): Context {
            val locale = Locale(language)
            Locale.setDefault(locale)
            val configuration = context.getResources().getConfiguration()
            configuration.setLocale(locale)
            configuration.setLayoutDirection(locale)
            return context.createConfigurationContext(configuration)
        }
      
        private fun updateResourcesLegacy(context: Context, language: String): Context {
            val locale = Locale(language)
            Locale.setDefault(locale)
            val resources = context.getResources()
            val configuration = resources.getConfiguration()
            configuration.locale = locale
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
                configuration.setLayoutDirection(locale)
            }
            resources.updateConfiguration(configuration, resources.getDisplayMetrics())
            return context
        }
      }
      

    【讨论】:

      【解决方案8】:

      我用这段代码解决了我的问题

      public void setLocale(String lang) {
      
              myLocale = new Locale(lang);
              Resources res = getResources();
              DisplayMetrics dm = res.getDisplayMetrics();
              Configuration conf = res.getConfiguration();
              conf.locale = myLocale;
              res.updateConfiguration(conf, dm);
      
              onConfigurationChanged(conf);
          }
      
      
      
          @Override
          public void onConfigurationChanged(Configuration newConfig) 
          {
              iv.setImageDrawable(getResources().getDrawable(R.drawable.keyboard));
              greet.setText(R.string.greet);
              textView1.setText(R.string.langselection);
      
              super.onConfigurationChanged(newConfig);
      
          }
      

      【讨论】:

      • 当我按下返回按钮时,前一个活动不会改变它的语言环境
      • 你的代码不好
      【解决方案9】:

      调用此方法更改应用区域设置:

      public void settingLocale(Context context, String language) {
      
          Locale locale;
      
          Configuration config = new Configuration();
      
           if(language.equals(LANGUAGE_ENGLISH)) {
      
              locale = new Locale("en");
      
              Locale.setDefault(locale);
      
              config.locale = locale;
      
          }else if(language.equals(LANGUAGE_ARABIC)){
      
              locale = new Locale("hi");
      
              Locale.setDefault(locale);
      
              config.locale = locale;
      
          }
      
          context.getResources().updateConfiguration(config, null);
      
          // Here again set the text on view to reflect locale change
      
          // and it will pick resource from new locale
      
          tv1.setText(R.string.one); //tv1 is textview in my activity
      
      }
      

      注意:将您的字符串放在 value 和 values- 文件夹中。

      【讨论】:

      • 完美答案...用于更改语言环境,无需像重启应用程序那样更新 UI,只需更改当前显示的语言
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-01-01
      • 2011-01-16
      • 1970-01-01
      • 2011-02-05
      • 2023-04-02
      相关资源
      最近更新 更多