Android-le langage WebView change brusquement sur Android N

j'ai une application multilingue avec l'anglais de langue primaire et l'arabe de langue secondaire.

Comme décrit dans la documentation,

  • j'ai ajouté android:supportsRtl="true" dans le manifeste.
  • j'ai changé toutes les propriétés xml left et right attributs start et end respectivement.
  • j'ai ajouté des chaînes de langue arabe dans strings-ar (et même pour les autres ressources.)

La configuration ci-dessus fonctionne correctement. Après la modification de la Localear-AE, texte arabe et ressources sont correctement affichés dans Mes activités.

Cependant, chaque fois que je navigue à une Activity avec un WebView et/ou un WebViewClient, la localisation, le texte et la mise en page brusquement revenir à l'appareil par défaut.

autres conseils:

  • cela se produit sur un Nexus 6P Android 7.0. Tout fonctionne correctement sur Android 6.0.1 et en dessous.
  • Le brusque changement dans les paramètres régionaux qui se passe quand je navigue vers un Activity qui a un WebView et/ou WebViewClient (et j'en ai plusieurs). Elle ne se produit dans aucune des autres activités.

Android 7.0 a la prise en charge multi-locale, permettant à l'utilisateur de définir plus d'un défaut paramètres régionaux. Donc si je mets la locale primaire à Locale.UK:

enter image description here

puis sur la navigation vers le WebView, la locale change de ar-AE en-GB.

modifications de L'API Android 7.0:

Comme indiqué dans le liste des modifications de l'API, de nouvelles méthodes relatives aux locales ont été ajoutées aux classes suivantes de L'API 24:

Locale:

Configuration:

cependant, je construis mon application avec API 23, et je n'utilise aucun de ces nouvelles méthodes.

en outre ...

  • le problème se produit également sur L'émulateur Nexus 6P.

  • pour obtenir la locale par défaut, j'utilise Locale.getDefault().

  • pour définir la locale par défaut, j'utilise le code suivant:

    public static void setLocale(Locale locale){
        Locale.setDefault(locale);
        Configuration config = new Configuration();
        config.setLocale(locale);
        Context context = MyApplication.getInstance();
        context.getResources().updateConfiguration(config,
                context.getResources().getDisplayMetrics());
    }
    

quelqu'un A rencontré ce problème avant? Quelle en est la raison, et comment puis-je résoudre cette?

Références:

1. natif RTL support in Android 4.2.

2.prise en charge Multilingue - la Langue et les paramètres Régionaux.

3.méfiez-vous de la locale par défaut.

45
demandé sur Y.S. 2016-11-03 12:57:49

8 réponses

réponse de Ted Hopp réussi à résoudre le problème, mais il n'a pas abordé la question de l' pourquoi cela se produit.

La raison en est que les modifications apportées à l' WebView class et son support dans Android 7.0.

Contexte:

AndroidWebView est construit en utilisant WebKit. Alors qu'il faisait à l'origine partie de L'AOSP, à partir de KitKat une décision a été faite spin off WebView dans un composant séparé appelé Système Android WebView. Il s'agit essentiellement d'une application Système Android qui vient pré-installé avec les appareils Android. Il est régulièrement mis à jour, tout comme D'autres applications système telles que les services Google Play et L'application Play Store. Vous pouvez voir dans la liste de vos système installé des applications:

Android System WebView

Android 7.0 change:

démarrage avec Android N, L'application Chrome sera utilisée pour rendre tout/tous WebView s dans des applications Android tierces. Dans les téléphones qui ont Android N Out-of-the-box, L'application du système de WebView Android n'est pas présent du tout. Dans les appareils qui ont reçu une mise à jour OTA vers Android N, Le système Android WebView est désactivé:

WebView disabled

et

WebView disabled

de plus, le support multi-locale a été introduit, avec des appareils ayant plus d'une langue par défaut:

enter image description here

Ceci a une conséquence importante pour les applications qui ont plusieurs langues. Si votre application a WebView s, puis ceux sont rendus en utilisant L'application Chrome. Parce que Chrome est une application Android en soi, tournant dans son propre processus sandboxed, il ne sera pas lié à la locale définie par votre application. Au lieu de cela, Chrome va revenir à la locale de périphérique primaire. Pour exemple, dites que votre locale d'application est définie à ar-AE, alors que la localisation principale de l'appareil est en-US. Dans ce cas, les paramètres régionaux de l' Activity contenant un WebView va changer de ar-AEen-US, et les chaînes et les ressources des dossiers des locales correspondantes seront affichées. Vous pouvez voir un mash-mash de chaînes/ressources LTR et RTL sur ceux Activitys qui ont WebView.

La Solution:

La solution complète à ce problème se compose de deux étapes:

STEP 1:

tout d'abord, réinitialisez la locale par défaut manuellement dans chaque Activity, ou au moins tous les Activity qui a un WebView.

public static void setLocale(Locale locale){
    Context context = MyApplication.getInstance();
    Resources resources = context.getResources();
    Configuration configuration = resources.getConfiguration();
    Locale.setDefault(locale);
    configuration.setLocale(locale);

    if (Build.VERSION.SDK_INT >= 25) {
        context = context.getApplicationContext().createConfigurationContext(configuration);
        context = context.createConfigurationContext(configuration);
    }

    context.getResources().updateConfiguration(configuration,
            resources.getDisplayMetrics());
}

Appel de la méthode ci-dessus avant d'appeler setContentView(...) dans le onCreate() méthode de toutes vos Activités. locale paramètre doit être la valeur par défaut Locale que vous souhaitez définir. Par exemple, si vous voulez définir Arabic/UAE comme locale par défaut, vous devez passer new Locale("ar", "AE"). Ou si vous souhaitez définir la locale par défaut (i.e. le Locale qu'est automatiquement définie par le système d'exploitation), vous devez passer Locale.US.

STEP 2:

en Outre, vous devez ajouter la ligne de code suivante:

new WebView(this).destroy();

dans le onCreate() de votre Application la classe (si vous en avez un), et partout ailleurs où l'utilisateur peut changer la langue. Cela prendra soin de toutes sortes de cas de bord qui peuvent se produire sur le redémarrage de l'application après avoir changé le langue (vous avez peut-être remarqué des chaînes de caractères dans d'autres langues ou avec l'alignement opposé après avoir changé la langue sur ActivitiesWebView s sur Android 7.0++).

en addendum, Chrome onglet personnalisé sont maintenant la façon préférée de rendre les pages Web in-app.

Références:

1.Android 7.0-modifications pour WebView.

2.comprendre les correctifs de sécurité WebView et Android.

3.WebView pour Android.

4.WebView: à Partir de "Powered by google Chrome" à droite de haut Chrome.

5. Nougat WebView.

6.Android 7.0 Nougat.

7.Android n Mystères, Partie 1: Système Android WebView est juste "Chrome" maintenant?.

47
répondu Y.S. 2018-09-10 13:41:50

votre code semble définir la locale dans la configuration de l'application elle-même (MyApplication.getInstance()). Cependant, vous devez mettre à jour la configuration pour le contexte d'activité avant de gonfler la vue de contenu de l'activité. J'ai trouvé que modifier le contexte de l'application n'est pas suffisant (et, comme il s'avère, n'est même pas nécessaire). Si Je ne mets pas à jour chaque contexte d'activité, alors le comportement est incohérent entre les activités.

la façon dont j'aborde cette question Est la sous-classe AppCompatActivity (ou Activity si ne pas utiliser la bibliothèque de compatibilité) et puis dériver toutes mes classes d'activité de cette sous-classe. Voici une version simplifiée de mon code:

public class LocaleSensitiveActivity extends AppCompatActivity {
    @Override protected void onCreate(Bundle savedInstanceState) {
        Locale locale = ... // the locale to use for this activity
        fixupLocale(this, locale);
        super.onCreate(savedInstanceState);
        ...
    }

    static void fixupLocale(Context ctx, Locale newLocale) {
        final Resources res = ctx.getResources();
        final Configuration config = res.getConfiguration();
        final Locale curLocale = getLocale(config);
        if (!curLocale.equals(newLocale)) {
            Locale.setDefault(newLocale);
            final Configuration conf = new Configuration(config);
            conf.setLocale(newLocale);
            res.updateConfiguration(conf, res.getDisplayMetrics());
        }
    }

    private static Locale getLocale(Configuration config) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            return config.getLocales().get(0);
        } else {
            //noinspection deprecation
            return config.locale;
        }
    }
}

Puis j'ai assurez-vous d'appeler super.onCreate(savedInstanceState) dans chaque sous-classe des onCreate() méthode avant appeler n'importe quelle méthode (telle que setContentView()) qui utilisent le contexte.

18
répondu Ted Hopp 2017-03-09 16:55:18

après avoir lu toutes les réponses, j'ai découvert qu'il manquait quelque chose dans chacune d'elles.voici donc la solution qui a fonctionné pour moi jusqu'à présent. Étant donné que WebView l'emporte sur la configuration de la langue du contexte de l'activité et du contexte de l'application, vous devez vous assurer que chaque fois que cela se produit, vous appelez une méthode qui réinitialise ces changements. Dans mon cas j'ai écrit la classe suivante que mes activités qui présentent ce problème s'étendent (ceux qui montrent un WebView):

public class WebViewFixAppCompatActivity extends AppCompatActivity {

private Locale mBackedUpLocale = null;

@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        mBackedUpLocale = getApplicationContext().getResources().getConfiguration().getLocales().get(0);
    }
}

@Override
protected void onStop() {
    super.onStop();
    fixLocale();
}

@Override
public void onBackPressed() {
    fixLocale();
    super.onBackPressed();
}

/**
 * The locale configuration of the activity context and the global application context gets overridden with the first language the app supports.
 */
public void fixLocale() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        Resources resources = getResources();
        final Configuration config = resources.getConfiguration();

        if (null != mBackedUpLocale && !config.getLocales().get(0).equals(mBackedUpLocale)) {
            Locale.setDefault(mBackedUpLocale);
            final Configuration newConfig = new Configuration(config);
            newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry()));
            resources.updateConfiguration(newConfig, null);
        }

        // Also this must be overridden, otherwise for example when opening a dialog the title could have one language and the content other, because
        // different contexts are used to get the resources.
        Resources appResources = getApplicationContext().getResources();
        final Configuration appConfig = appResources.getConfiguration();
        if (null != mBackedUpLocale && !appConfig.getLocales().get(0).equals(mBackedUpLocale)) {
            Locale.setDefault(mBackedUpLocale);
            final Configuration newConfig = new Configuration(appConfig);
            newConfig.setLocale(new Locale(mBackedUpLocale.getLanguage(), mBackedUpLocale.getCountry()));
            appResources.updateConfiguration(newConfig, null);
        }

    }
}
}

l'idée postée par @Tobliug pour sauvegarder la configuration initiale avant que le WebView ne l'emporte, cela a fonctionné pour moi, dans mon cas particulier j'ai trouvé que c'était plus facile à implémenter que d'autres solutions postées. Il est Important que la méthode fix soit appelée après la sortie de WebView, par exemple en appuyant sur le dos et dans onStop. Si le webView est affiché dans une boîte de dialogue, vous devez faire attention à ce que la méthode fix soit appelée après avoir rejeté la boîte de dialogue, principalement dans onResume et/ou onCreate. Et si le webView est directement chargé dans onCreate de L'activité et non plus par la suite dans un nouveau fragment, le correctif doit aussi être appelé directement après setContentView avant que le titre de l'activité soit défini, etc. Si le WebView est chargé à l'intérieur d'un fragment dans l'activité, appelez l'activité dans onViewCreated du fragment et l'activité devrait appeler la méthode fix. Toutes les activités n'ont pas besoin d'étendre la classe ci-dessus, comme indiqué dans un aswer, c'est un surmenage et pas nécessaire. Ce problème ne se résout pas non plus en remplaçant le WebView par des onglets GoogleChrome ou l'ouverture d'un navigateur externe.

si vous avez vraiment besoin de configurer vos ressources pour avoir la liste complète des langues et pas seulement une, alors vous devez fusionner cette solution avec celle de https://gist.github.com/amake/0ac7724681ac1c178c6f95a5b09f03ce Dans mon cas, il n'était pas nécessaire.

Je n'ai pas non plus trouvé nécessaire d'appeler new WebView(ceci).destroy (), comme indiqué dans une réponse ici.

2
répondu David 2018-03-15 17:03:35

Même problème ici. J'ai une solution sale, mais simple.

parce que j'observe que le lieu est encore bon dans l'activité.onCreate(...) fonction et non plus dans l'Activité.onPostCreate(...), je sauve juste la Locale et la force à la fin de la onPostCreate (...) fonction.

la Voici :

private Locale backedUpLocale = null;

@Override
protected void onCreate(final Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    backedUpLocale = getApplicationContext().getResources().getConfiguration().locale;
}

@Override
protected void onPostCreate(@Nullable Bundle savedInstanceState) {
    super.onPostCreate(savedInstanceState);
    changeLocale(backedUpLocale);
}

Bonus-la fonction change locale:

public void changeLocale(final Locale locale) {

    final Configuration config = res.getConfiguration();

    if(null != locale && !config.locale.equals(locale)) {
        Locale.setDefault(locale);

        final Configuration newConfig = new Configuration(config);

        if(PlatformVersion.isAtLeastJellyBeanMR1()) {
            newConfig.setLocale(new Locale(locale.getLanguage()));
        } else {
            newConfig.locale = new Locale(locale.getLanguage());
        }

        res.updateConfiguration(newConfig, null);
    }
}

espère que cela aidera.

1
répondu Tobliug 2017-09-05 14:15:33

aucune des réponses ci-dessus ne m'a aidé, j'ai réussi à réinitialiser app locale à nouveau à l'intérieur onStop () méthode de l'activité contenant la Webview

0
répondu MohammadReza 2017-02-22 14:23:59

si vous utilisez le WebView uniquement pour afficher du texte riche (du texte avec certains paragraphes ou du texte en gras et en italique dans différentes tailles de police), alors vous pouvez utiliser TextView et Html.fromHtml () à la place. TextViews n'ai pas de problème avec les paramètres régionaux ;-)

0
répondu Ben 2017-09-18 14:04:34

je veux en ajouter un de plus de cas d'utilisation ici:

lorsque vous appuyez sur Retour de l'activité de webview (c.-à-d. affichage de l'écran de paiement et bouton de retour de l'utilisateur), onCreate () de l'activité précédente ne s'exécute pas, de sorte que le langage a été réinitialisé à nouveau. Pour le garder libre de tout bug, nous devons réinitialiser app locale dans onResume() De L'activité de base.

private static void updateResources(Context context, String language) {
    Locale locale = new Locale(language);
    Locale.setDefault(locale);
    Configuration config = new Configuration();
    config.setLocale(locale);
    config.setLayoutDirection(locale);
    context.getResources().updateConfiguration(config,
            context.getResources().getDisplayMetrics());
}

méthode de L'appel ci-dessus dans onResume () de l'activité de base ou du moins dans webview activité.

Edit: Si vous avez affaire à des Fragments, assurez-vous que cette méthode a appelé lorsque l'utilisateur sort de webview.

0
répondu Akhilesh Dhar Dubey 2018-03-16 06:57:14

dans Android N, quand vous faites new WebView(), il ajoutera /system/app/WebViewGoogle/WebViewGoogle.apk chemin d'accès aux ressources, et si elle n'avait pas ajouté au path, il va provoquer des Ressources recréer.

alors si vous voulez résoudre la question, faites new WebView(application) en application avant de changer le local.

Si vous ne savez Chinois, vous pouvez lire ce blog.

-1
répondu cunhan.fch 2017-08-18 03:47:40