Los recursos y la dirección de diseño se representan incorrectamente solo en Android 8.0 y superior
android-layout locale (6)
Cerré totalmente la aplicación porque creo que está haciendo caché en segundo plano.
Use el código a continuación, así es como lo logré en mi caso, también puede intentarlo:
Intent mStartActivity = new Intent(ctc, SplashActivity.class);
int mPendingIntentId = 123456;
PendingIntent mPendingIntent = PendingIntent.getActivity(ctc, mPendingIntentId, mStartActivity, PendingIntent.FLAG_CANCEL_CURRENT);
AlarmManager mgr = (AlarmManager)ctc.getSystemService(Context.ALARM_SERVICE);
mgr.set(AlarmManager.RTC, System.currentTimeMillis() + 100, mPendingIntent);
System.exit(0);
Tengo una aplicación multilingüe con el idioma principal inglés y el idioma secundario árabe.
setLocale()
a
setLocale()
en
onCreate()
de cada
Activity
en mi aplicación:
public static void setLocale(Locale locale){
Locale.setDefault(locale);
Context context = MyApplication.getInstance();
final Resources resources = context.getResources();
final Configuration config = resources.getConfiguration();
config.setLocale(locale);
context.getResources().updateConfiguration(config,
resources.getDisplayMetrics());
}
donde
locale
es uno de los siguientes:
Se llama al método anterior antes de que se llame a
super.onCreate(savedInstanceState)
.
Como se describe en la documentation ,
-
He agregado
android:supportsRtl="true"
en el manifiesto. -
He cambiado todas las propiedades xml con atributos
left
yright
parastart
yend
respectivamente. -
He puesto cadenas en idioma árabe en la carpeta
res/values-ar/strings
y recursos extraíbles en la carpetares/drawable-ar
(y de manera similar para otros recursos).
La configuración anterior funciona correctamente.
Después de cambiar la
Locale
a
ar-AE
, el texto y los recursos en árabe se muestran correctamente en mis actividades.
Sin embargo, hay un problema con los recursos y la dirección de diseño para todos los dispositivos Android con la versión 8.0 y superior.
En un dispositivo con versión inferior a 8.0, una pantalla RTL se ve correctamente así:
Y en todos los dispositivos con 8.0+, la misma pantalla aparece así:
que esta mal
Resulta que tanto la dirección como los recursos se muestran incorrectamente.
Aquí hay dos problemas:
-
La configuración
Locale
correcta no parece actualizarse en la configuración de la aplicación. - La dirección del texto y los dibujos son opuestos a lo que debería ser.
Con respecto a la dirección, existe un método curioso llamado
setLayoutDirection()
que no había notado antes.
Me gustaría saber cuál es este problema, por qué ocurre en Oreo y cuál es la solución. Por favor ayuda / comenta sobre esto.
EDITAR :
De acuerdo con el informe de diferencias de API , el método
updateConfiguration()
fue depreciado en Android 7.1 (nivel de API 25).
Además, encontré todas las publicaciones relevantes sobre esto. En orden de importancia:
1. Android N cambia el idioma mediante programación .
2. Android context.getResources.updateConfiguration () en desuso .
3. Cómo cambiar el idioma de la aplicación Android O / Oreo / api 26 .
4. Problema de Android RTL en API 24 y superior en cambio de configuración regional
5. Cambiar el lenguaje mediante programación (Android N 7.0 - API 24) .
6. Android N - Cambiar configuración regional en tiempo de ejecución .
7. Error de diseño RTL en Android Oreo .
El método updateConfiguration() está en desuso en el nivel 25 de API .
El documento sugiere usar createConfigurationContext()
Simplemente puede hacer una actividad base que es un padre común de todas las actividades como se muestra a continuación.
public class BaseActivity
extends AppCompatActivity {
private static final String LANGUAGE_CODE_ENGLISH = "en";
private static final String LANGUAGE_CODE_ARABIC = "ar";
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CommonUtils.getLanguageAwareContext(newBase));
}
private static Context getLanguageAwareContext(Context context) {
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(new Locale(getLanguageCode));
return context.createConfigurationContext(configuration);
}
// Rewrite this method according to your needs
private static String getLanguageCode() {
return LANGUAGE_CODE_ARABIC;
}
}
Notas
-
getLanguageCode()
debería devolver el código de idioma. Normalmente, el código de idioma o cualquier otro dato que lo represente se almacena en las preferencias. - Para cambiar los idiomas dinámicamente, vuelva a crear actividad después de configurar el código de idioma apropiado en las preferencias.
-
Use el contexto de actividad en lugar del contexto de la aplicación para acceder a los recursos específicos de la localidad.
En otras palabras, use
this
oActivityName.this
de las actividades ygetActivity()
de los fragmentos en lugar degetApplicationContext()
.
El método
updateConfiguration()
fue
obsoleto
Ahora necesitamos usar
createConfigurationContext()
Me las he arreglado de esta manera
crear una nueva clase
ContextWrapper
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.os.Build;
import android.os.LocaleList;
import java.util.Locale;
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 (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
configuration.setLocale(newLocale);
LocaleList localeList = new LocaleList(newLocale);
LocaleList.setDefault(localeList);
configuration.setLocales(localeList);
context = context.createConfigurationContext(configuration);
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
configuration.setLocale(newLocale);
context = context.createConfigurationContext(configuration);
} else {
configuration.locale = newLocale;
res.updateConfiguration(configuration, res.getDisplayMetrics());
}
return new ContextWrapper(context);
}}
crear una nueva clase de
BaseActivity
import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import java.util.Locale;
/**
* Created by nilesh on 20/3/18.
*/
public class BaseActivity extends AppCompatActivity {
@Override
protected void attachBaseContext(Context newBase) {
Locale newLocale;
String lang = new PrefManager(newBase).getLanguage();
if (lang.equals("zh_CN")) {
newLocale = new Locale("zh");
} else {
newLocale = new Locale(lang);
}
Context context = ContextWrapper.wrap(newBase, newLocale);
super.attachBaseContext(context);
}
}
Cree una clase
PrefManager
para almacenar la configuración regional
import android.content.Context;
import android.content.SharedPreferences;
public class PrefManager {
private SharedPreferences.Editor editor;
private Context mContext;
private SharedPreferences prefs;
private final String LANGUAGE = "language";
private final String PREF = "user_data";
public PrefManager(Context mContext) {
this.mContext = mContext;
}
public String getLanguage() {
this.prefs = this.mContext.getSharedPreferences(PREF, 0);
return this.prefs.getString(LANGUAGE, "en_US");
}
public void setLanguage(String language) {
this.editor = this.mContext.getSharedPreferences(PREF, 0).edit();
this.editor.putString(LANGUAGE, language);
this.editor.apply();
}
}
Ahora necesita extender su BaseActivity en todas sus actividades como
public class OrdersActivity extends BaseActivity
Ahora, cuando necesite cambiar la
PrefManager
Locale
simplemente actualice el valor enPrefManager
y reinicie su actividad
PrefManager prefManager= new PrefManager(this);
prefManager.setLanguage("zh_CN");
// restart your activity
NOTA
La solución completa a este problema consta de tres pasos:
PASO 1 :
En
onCreate()
de su
BaseActivity
(o todas sus
Activity
), configure la configuración
Locale
siguiente manera:
@Override
protected void onCreate(Bundle savedInstanceState) {
// set the Locale the very first thing
Utils.setLocale(Utils.getSavedLocale());
requestWindowFeature(Window.FEATURE_NO_TITLE);
super.onCreate(savedInstanceState);
......
......
}
donde
getSavedLocale()
es la
getSavedLocale()
Locale
correspondiente a la región actual (esto será específico para su proyecto ...).
Y el método
Utils.setLocale(...)
se define de la siguiente manera:
public static void setLocale(Locale locale){
Context context = MyApplication.getInstance();
Resources resources = context.getResources();
Configuration configuration = resources.getConfiguration();
Locale.setDefault(locale);
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
// updateConfiguration(...) is deprecated in N
if (Build.VERSION.SDK_INT >= 25) {
context = context.getApplicationContext().createConfigurationContext(configuration);
context = context.createConfigurationContext(configuration);
}
context.getResources().updateConfiguration(configuration,
resources.getDisplayMetrics());
}
Esto establece la configuración
Locale
correcta en cada
Activity
.
Esto es suficiente para las aplicaciones que admiten el nivel 25 de API. Para el nivel 26 y superior de API, también se requieren los PASOS 2 y 3.
PASO 2 :
Anule el siguiente método en su
BaseActivity
:
@Override
protected void attachBaseContext(Context newBase) {
newBase = Utils.getLanguageAwareContext(newBase);
super.attachBaseContext(newBase);
}
donde la función
getLanguageAwareContext(...)
se define de la siguiente manera:
public static Context getLanguageAwareContext(Context context){
Configuration configuration = context.getResources().getConfiguration();
Locale locale = getIntendedLocale();
configuration.setLocale(locale);
configuration.setLayoutDirection(locale);
return context.createConfigurationContext(configuration);
}
Esto, junto con el PASO 1, establece la configuración
Locale
correcta en cada
Activity
de su aplicación para el nivel de API 26 y superior.
Sin embargo, se requiere un paso más para configurar la dirección del idioma correctamente ...
PASO 3 :
En
onCreate()
de su
BaseActivity
, agregue el siguiente código:
@Override
protected void onCreate(Bundle savedInstanceState) {
....
....
// yup, it''s a legit bug ... :)
if (Build.VERSION.SDK_INT >= 26) {
getWindow().getDecorView().setLayoutDirection(Utils.isRTL()
? View.LAYOUT_DIRECTION_RTL : View.LAYOUT_DIRECTION_LTR);
}
....
....
}
donde la función
isRTL()
se define de la siguiente manera:
public static boolean isRTL(){
return TextUtilsCompat.getLayoutDirectionFromLocale(Locale.getDefault()) == View.LAYOUT_DIRECTION_RTL;
}
Los pasos anteriores deben ocuparse de todos los problemas (al menos con respecto a la configuración de la dirección
Locale
y del texto) en todas las versiones existentes de Android.
Resources.updateConfiguration está en desuso, use esto en su lugar:
fun setLocale(old: Context, locale: Locale): Context {
val oldConfig = old.resources.configuration
oldConfig.setLocale(locale)
return old.createConfigurationContext(oldConfig)
}
override fun attachBaseContext(newBase: Context?) {
super.attachBaseContext(newBase?.let { setLocale(it, Locale("ar")) })
}
En java
private Context setLocale(Context old, Locale locale) {
Configuration oldConfig = old.getResources().getConfiguration();
oldConfig.setLocale(locale);
return old.createConfigurationContext(oldConfig);
}
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(setLocale(newBase, new Locale("ar")));
}
public void setLocale(final Context ctx, final String lang) {
AppSettings.getInstance(ctx).save(PrefKeys.language, lang);
final Locale loc = new Locale(lang);
Locale.setDefault(loc);
final Configuration cfg = new Configuration();
cfg.locale = loc;
ctx.getResources().updateConfiguration(cfg, null);
}
Cambiar a inglés:
setLocale(getActivity(), "en";
Cambiar a árabe:
setLocale(getActivity(), "ar");
Después de esto, debe reiniciar la aplicación para obtener los efectos de cambio de idioma.