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
leftyrightparastartyendrespectivamente. -
He puesto cadenas en idioma árabe en la carpeta
res/values-ar/stringsy 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
Localecorrecta 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
thisoActivityName.thisde 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
PrefManagerpara 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
PrefManagerLocalesimplemente actualice el valor enPrefManagery 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.