android android-layout locale android-8.0-oreo

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 y right para start y end respectivamente.
  • He puesto cadenas en idioma árabe en la carpeta res/values-ar/strings y recursos extraíbles en la carpeta res/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 o ActivityName.this de las actividades y getActivity() de los fragmentos en lugar de getApplicationContext() .

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 en PrefManager y reinicie su actividad

PrefManager prefManager= new PrefManager(this); prefManager.setLanguage("zh_CN"); // restart your activity

NOTA

Puede descargar el código fuente del repositorio de github


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.