Android N cambia el lenguaje programáticamente

Encontré errores realmente extraños que se reproducen solo en dispositivos con Android N.

En el recorrido por mi aplicación, existe la posibilidad de cambiar el idioma. Aquí está el código que lo cambia.

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

Este código funciona muy bien en la actividad de mi recorrido (con la llamada a recreate() ) pero en todas las actividades siguientes, todos los recursos de String son incorrectos. La rotación de la pantalla lo arregla. ¿Qué puedo hacer con este problema? ¿Debo cambiar la configuración regional para Android N de manera diferente o solo es un error del sistema?

PD Esto es lo que encontré. Al principio de MainActivity (que está después de mi recorrido) Locale.getDefault() es correcto pero los recursos son incorrectos. Pero en otras actividades me da mal la configuración regional y los recursos incorrectos de esta localidad. Después de la pantalla de rotación (o quizás algún otro cambio de configuración) Locale.getDefault() es correcto.

De acuerdo. Finalmente logré encontrar una solución.

Primero debe saber que en 25 API Resources.updateConfiguration(...) está en desuso. Entonces, en cambio, puedes hacer algo como esto:

1) Necesita crear su propio ContextWrapper que anule todos los parámetros de configuración en baseContext. Por ejemplo, este es el ContextWrapper mío que cambia la configuración regional correctamente. Preste atención en el método context.createConfigurationContext(configuration) .

 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) Esto es lo que debe hacer en su BaseActivity:

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

Nota:

Recuerde recrear su actividad si desea cambiar la configuración regional en su aplicación en algún lugar. Puede anular cualquier configuración que desee utilizando esta solución.

Inspirado por varios códigos (es decir, nuestros hermanos Stackoverflow (gritar chicos)), había producido una versión mucho más simple. La extensión ContextWrapper es innecesaria.

Primero digamos que tiene 2 botones para 2 idiomas, EN y KH. En onClick para los botones, guarde el código de idioma en SharedPreferences , luego llame al método recreate() la actividad.

Ejemplo:

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

Luego crea un método estático que devuelva ContextWrapper , tal vez en una clase de Utils (porque eso es lo que hice, 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); } 

Finalmente, cargue el código de idioma de SharedPreferences en el método attachBaseContext(Context newBase) ALL ACTIVITY .

 @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: Para ahorrar sudar a la palma en el teclado, creé una clase LangSupportBaseActivity que amplía la Activity y usa el último fragmento de código allí. Y tengo todas las demás actividades extiende LangSupportBaseActivity .

Ejemplo:

 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 }