J'ai un ancien projet qui prend en charge plusieurs langues. Je veux mettre à jour la bibliothèque de support et la plate-forme cible, Avant de migrer vers Androidx
tout fonctionne bien mais maintenant changer de langue ne fonctionne pas!
J'utilise ce code pour modifier les paramètres régionaux par défaut de l'application
private static Context updateResources(Context context, String language)
{
Locale locale = new Locale(language);
Locale.setDefault(locale);
Configuration configuration = context.getResources().getConfiguration();
configuration.setLocale(locale);
return context.createConfigurationContext(configuration);
}
Et appelez cette méthode sur chaque activité en remplaçant attachBaseContext
comme ceci:
@Override
protected void attachBaseContext(Context newBase)
{
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
String language = preferences.getString(SELECTED_LANGUAGE, "fa");
super.attachBaseContext(updateResources(newBase, language));
}
J'essaie une autre méthode pour obtenir une chaîne et j'ai remarqué que getActivity().getBaseContext().getString
fonctionne et getActivity().getString
ne fonctionne pas. Même le code suivant ne fonctionne pas et affiche toujours app_name
Vlaue dans la ressource par défaut string.xml.
<TextView
Android:id="@+id/textView"
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:text="@string/app_name"/>
Je partage un exemple de code dans https://github.com/Freydoonk/LanguageTest
getActivity()..getResources().getIdentifier
ne fonctionne pas non plus et renvoie toujours 0!
Enfin, je trouve le problème dans mon application. Lors de la migration du projet vers Androidx
les dépendances de mon projet ont changé comme ceci:
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.1.0-alpha03'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.Android.material:material:1.1.0-alpha04'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0-alpha02'
}
Comme on le voit, la version de androidx.appcompat:appcompat
est 1.1.0-alpha03
lorsque je l'ai changé pour la dernière version stable, 1.0.2
, mon problème est résolu et le changement de langue fonctionne correctement.
Je trouve la dernière version stable de la bibliothèque appcompat
dans Maven Repository . Je change également d'autres bibliothèques pour la dernière version stable.
Maintenant, ma section des dépendances d'application est comme ci-dessous:
dependencies {
implementation fileTree(include: ['*.jar'], dir: 'libs')
implementation 'androidx.appcompat:appcompat:1.0.2'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
implementation 'com.google.Android.material:material:1.0.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.1.1'
}
Fondamentalement, ce qui se passe en arrière-plan, c'est que pendant que vous avez correctement défini la configuration dans attachBaseContext
, le AppCompatDelegateImpl
va alors et remplace la configuration par une configuration complètement nouvelle sans paramètres régionaux:
final Configuration conf = new Configuration();
conf.uiMode = newNightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
try {
...
((Android.view.ContextThemeWrapper) mHost).applyOverrideConfiguration(conf);
handled = true;
} catch (IllegalStateException e) {
...
}
Dans un commit non publié de Chris Banes cela a été corrigé: la nouvelle configuration est une copie complète de la configuration du contexte de base.
final Configuration conf = new Configuration(baseConfiguration);
conf.uiMode = newNightMode | (conf.uiMode & ~Configuration.UI_MODE_NIGHT_MASK);
try {
...
((Android.view.ContextThemeWrapper) mHost).applyOverrideConfiguration(conf);
handled = true;
} catch (IllegalStateException e) {
...
}
Jusqu'à ce que cela soit publié, il est possible de faire exactement la même chose manuellement. Pour continuer à utiliser la version 1.1.0, ajoutez ceci sous votre attachBaseContext
:
Solution Kotlin
override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
if (overrideConfiguration != null) {
val uiMode = overrideConfiguration.uiMode
overrideConfiguration.setTo(baseContext.resources.configuration)
overrideConfiguration.uiMode = uiMode
}
super.applyOverrideConfiguration(overrideConfiguration)
}
Solution Java
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (overrideConfiguration != null) {
int uiMode = overrideConfiguration.uiMode;
overrideConfiguration.setTo(getBaseContext().getResources().getConfiguration());
overrideConfiguration.uiMode = uiMode;
}
super.applyOverrideConfiguration(overrideConfiguration);
}
Ce code fait exactement la même chose que Configuration(baseConfiguration)
fait sous le capot, mais parce que nous le faisons après le AppCompatDelegate
a déjà défini le bon uiMode
, nous devons nous assurer de prendre le uiMode
remplacé après que nous l'avons corrigé afin de ne pas perdre le réglage du mode sombre/clair.
Veuillez noter que cela ne fonctionne que si vous ne spécifiez pas configChanges="uiMode"
Dans votre manifeste . Si vous le faites, il y a encore un autre bug: à l'intérieur de onConfigurationChanged
, le newConfig.uiMode
Ne sera pas défini par AppCompatDelegateImpl
's onConfigurationChanged
. Cela peut également être résolu si vous copiez tout le code que AppCompatDelegateImpl
utilise pour calculer le mode de nuit actuel dans votre code d'activité de base, puis le remplacez avant l'appel super.onConfigurationChanged
. À Kotlin, cela ressemblerait à ceci:
private var activityHandlesUiMode = false
private var activityHandlesUiModeChecked = false
private val isActivityManifestHandlingUiMode: Boolean
get() {
if (!activityHandlesUiModeChecked) {
val pm = packageManager ?: return false
activityHandlesUiMode = try {
val info = pm.getActivityInfo(ComponentName(this, javaClass), 0)
info.configChanges and ActivityInfo.CONFIG_UI_MODE != 0
} catch (e: PackageManager.NameNotFoundException) {
false
}
}
activityHandlesUiModeChecked = true
return activityHandlesUiMode
}
override fun onConfigurationChanged(newConfig: Configuration) {
if (isActivityManifestHandlingUiMode) {
val nightMode = if (delegate.localNightMode != AppCompatDelegate.MODE_NIGHT_UNSPECIFIED)
delegate.localNightMode
else
AppCompatDelegate.getDefaultNightMode()
val configNightMode = when (nightMode) {
AppCompatDelegate.MODE_NIGHT_YES -> Configuration.UI_MODE_NIGHT_YES
AppCompatDelegate.MODE_NIGHT_NO -> Configuration.UI_MODE_NIGHT_NO
else -> applicationContext.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK
}
newConfig.uiMode = configNightMode or (newConfig.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv())
}
super.onConfigurationChanged(newConfig)
}
MISE À JOUR 16 décembre:
La version récente de AppCompatDelegateImpl.Java
a reçu de nombreux autres changements depuis la rédaction initiale de cet article. Le correctif de Chris Banes que je mentionne ci-dessus n'était apparemment pas suffisant pour se débarrasser complètement de tous les bogues liés à configChanges
(vous pouvez voir ma solution ci-dessus pour configChanges="uiMode"
, Et un utilisateur l'a souligné dans le commente ici que dans son cas, la valeur orientation
ne changeait pas dans onConfigurationChanged
). La dernière implémentation s'appuie à la place sur une nouvelle méthode appelée generateConfigDelta
pour créer l'instance de configuration correcte, de sorte que toute autre manière de gérer le mode nuit est en fait obsolète.
Personnellement, j'utilise ma solution depuis des mois sans aucun problème ni aucun utilisateur se plaignant, mais vous devez toujours être conscient que AppCompatDelegateImpl
est toujours en cours de développement, veuillez donc utiliser ma solution à votre discrétion. Assurez-vous également de consulter les autres réponses qui suggèrent une rétrogradation vers les versions de bibliothèque antérieures, qui pourrait seraient plus appropriées dans votre situation.
Il y a un problème dans les nouvelles bibliothèques de compatibilité des applications liées au mode nuit qui entraîne la substitution de la configuration sur Android 21 à 25. Cela peut être résolu en appliquant votre configuration lorsque cette fonction publique est appelée:
public void applyOverrideConfiguration (Configuration overrideConfiguration
Pour moi, cette petite astuce a fonctionné en copiant les paramètres de la configuration remplacée dans ma configuration, mais vous pouvez faire ce que vous voulez. Il est préférable de réappliquer votre logique de langage à la nouvelle configuration pour minimiser les erreurs
@Override
public void applyOverrideConfiguration(Configuration overrideConfiguration) {
if (Build.VERSION.SDK_INT >= 21&& Build.VERSION.SDK_INT <= 25) {
//Use you logic to update overrideConfiguration locale
Locale locale = getLocale()//your own implementation here;
overrideConfiguration.setLocale(locale);
}
super.applyOverrideConfiguration(overrideConfiguration);
}
Enfin, j'ai obtenu une solution pour localiser, dans mon cas, le problème était en fait avec bundle apk
car il a divisé les fichiers de localisation. Dans bundle apk
par défaut, toutes les divisions seront générées. mais dans le bloc Android de votre build.gradle
fichier, vous pouvez déclarer les divisions qui seront générées.
bundle {
language {
// Specifies that the app bundle should not support
// configuration APKs for language resources. These
// resources are instead packaged with each base and
// dynamic feature APK.
enableSplit = false
}
}
Après avoir ajouté ce code à bloc Android de build.gradle
fichier mon problème est résolu.
Maintenant, il existe une version plus récente qui fonctionne également:
implementation 'androidx.appcompat:appcompat:1.1.0-alpha04'
Comme @Fred l'a mentionné appcompat:1.1.0-alpha03
a un problème bien qu'il ne soit pas mentionné sur leur journal des versions
Eu le même bug sur androidx.appcompat:appcompat:1.1.0
. Passé à androidx.appcompat:appcompat:1.1.0-rc01
et maintenant les langages changent le Android 5-6.