51 votes

Le WebView Android est blanc, la vue n'est pas mise à jour en cas de modification du code CSS ou du code HTML, les animations sont saccadées.

Lorsque je modifie une classe css, les changements n'apparaissent pas toujours. Cela semble se produire lorsque j'ai un événement tactile qui ajoute quelque chose comme un nom de classe vers le bas à un bouton. Le bouton ne se met pas à jour, pas plus que les autres éléments de la page. Le fonctionnement est très irrégulier. J'ai également remarqué que mes éléments apparaissent parfois en blanc, sans contenu ni rien. C'est très frustrant !

58voto

Kyle Points 771

Note :
Il y a une meilleure solution à partir d'Android 4.4+. Il s'agit d'un drop-in WebView remplacement appelé CrossWalk . Il utilise la dernière version de Chromium-kit et il est fantastique. Vous pouvez en savoir plus à ce sujet ici : projet-passerelle.org

De plus, il semble que depuis Android 4.4, la invalidate() n'est plus nécessaire et vous pouvez vous en sortir en utilisant d'autres solutions plus sûres. Je n'utiliserais que ceci invalidate() comme un dernier effort.


Je réponds à ma propre question dans l'espoir d'aider les personnes qui ont les mêmes problèmes que moi.

J'ai essayé plusieurs méthodes pour améliorer les choses, même la fameuse - webkit-transform: translate3d(0,0,0); Même ça, ça n'a pas très bien marché.

Laissez-moi partager avec vous ce qui a fonctionné.

Tout d'abord, utilisez l'API la plus récente. J'utilise l'API 15. Dans votre AndroidManifest.xml assurez-vous d'activer accélération matérielle . Si votre version de l'API ne prend pas en charge cette fonction, passez à l'étape suivante.

Si votre version le prend en charge, vous pouvez l'activer en modifiant votre manifeste :

<application
   ...
   android:hardwareAccelerated="true">

Assurez-vous également que votre manifeste possède l'API minimale supportée par celle que vous utilisez. Comme j'utilise l'API 15, voici ce que contient mon manifeste :

<uses-sdk
    android:minSdkVersion="15"
    android:targetSdkVersion="15" />

( Mise à jour vous devez maintenant modifier ces valeurs dans votre build.gradle )

Maintenant, dans votre fichier CSS principal pour ce qui sera présenté dans une WebView, ajoutez quelque chose comme ceci :

body div {
    -webkit-transform: translate3d(0,0,0);
}

Ajoutez à l'élément div body tout autre type d'élément dont vous disposez ; vous pouvez probablement exclure les images, ul , li etc. La raison de l'application de ce style CSS à tous les éléments est le résultat d'essais et d'erreurs, et j'ai constaté que l'application brute à tous les éléments semble fonctionner le mieux. Avec un arbre DOM plus grand, vous devrez peut-être être plus spécifique. Je ne suis pas sûr de la spécification, cependant.

Lorsque vous instanciez votre WebView il y a certains paramètres que vous devez définir :

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    super.loadUrl("file:///android_asset/www/index.html");
    appView.getSettings().setRenderPriority(RenderPriority.HIGH);
    appView.getSettings()
            .setPluginState(WebSettings.PluginState.ON_DEMAND);
}

Avant-dernière, mais cruciale partie : J'étais en train de lire le code source du WebView et j'ai trouvé ce petit commentaire minuscule sur le redécoupage forcé. Il existe un static final boolean qui, lorsqu'il est réglé sur true obligera la vue à toujours se redessiner. Je ne suis pas très au fait de la syntaxe Java, mais je ne pense pas que l'on puisse modifier directement un attribut statique final d'une classe. Donc, ce que j'ai fini par faire, c'est d'étendre la classe comme suit :

import org.apache.cordova.CordovaWebView;

import android.content.Context;
import android.graphics.Canvas;

public class MyWebView extends CordovaWebView {
    public static final String TAG = "MyWebView";

    public MyWebView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        // Warning! This will cause the WebView to continuously be redrawn
        // and will drain the devices battery while the view is displayed!
        invalidate();
    }

}

Gardez à l'esprit que j'utilise Cordova/PhoneGap, et que j'ai donc dû m'étendre à partir de la base de données de l'UE. CordovaWebView . Si vous voyez dans la méthode onDraw, il y a un appel à invalidate . La vue sera ainsi constamment redessinée. Je vous recommande vivement d'ajouter une logique permettant de ne redessiner que lorsque vous en avez besoin.

Il y a une dernière étape, si vous utilisez Cordova. Vous devez indiquer à PhoneGap d'utiliser votre nouvelle application WebView au lieu de leur propre classe WebView classe. Dans votre MainActivity ajoutez ceci :

public void init(){
    CordovaWebView webView = new MyWebView(MainActivity.this);
    super.init(webView, new CordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
}

C'est tout ! Essayez d'exécuter votre application et voyez si tout est beaucoup plus fluide. Avant d'effectuer tous ces changements, les pages apparaissaient en blanc, aucun changement CSS n'était appliqué jusqu'à ce que vous tapotiez à nouveau sur l'écran, les animations étaient très irrégulières ou même imperceptibles. Je dois préciser que les animations sont toujours hachées, mais beaucoup moins qu'avant.

Si quelqu'un a quelque chose à ajouter à cela, il suffit de le commenter ci-dessous. Je suis toujours ouvert aux optimisations ; et je suis pleinement conscient qu'il peut y avoir de meilleures façons de faire ce que je viens de recommander.

Si ma solution ci-dessus n'a pas fonctionné pour vous, pourriez-vous décrire votre situation spécifique et les résultats que vous obtenez avec les Androïdes. WebView ?

Enfin, j'ai activé cette réponse comme un "wiki communautaire" alors n'hésitez pas à faire des ajustements.

Gracias.


Editar:

Avec la dernière version de PhoneGap, votre méthode init() devra ressembler davantage à ceci :

public void init(){
    CordovaWebView webView = new MyWebView(MainActivity.this);
    super.init(webView, new IceCreamCordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
}

4 votes

Je vais demander à Joe de regarder votre réponse et peut-être que ce serait une bonne idée à inclure dans le code de CordovaWebView.

0 votes

Au fait... tout ceci est sur le point de changer encore une fois, Google est sur le point de changer le composant WebView pour utiliser Chromium, donc... Je suis vraiment impatient de voir ce qui sera cassé à nouveau... Pour moi, le passage de HoneyComb à ICS a été un désastre absolu. J'ai peur :S :S :S

11 votes

Il est à noter que l'appel à l'invalidation lors de l'appel au dessin signifie que votre vue Web sera redessinée en permanence. Ce n'est pas la meilleure idée.

17voto

Olivier Points 101

J'ai mis en œuvre la solution de Kyle et cela a résolu le problème. Cependant, j'ai remarqué une énorme consommation de batterie sous Android 4.0.4 lorsque l'application était ouverte. De plus, après le changement, des utilisateurs se sont plaints que le clavier swiftKey ne fonctionnait plus avec mon application.

Chaque changement dans mon application est déclenché par une action de l'utilisateur. J'ai donc créé une version modifiée qui ne déclenche invalidate() qu'après un touchEvent :

    Handler handler = new Handler();
    public boolean onTouchEvent (MotionEvent event){
        super.onTouchEvent(event);
        handler.postDelayed(triggerInvalidate, 60);
            handler.postDelayed(triggerInvalidate, 300);
        return true;
    }

    private Runnable triggerInvalidate=new Runnable(){
        public void run(){
            invalidate();
        }
    };

Je n'ai jamais fait de programmation avec Java, il y a donc peut-être une meilleure solution pour faire cela.

1 votes

Très belle approche ! !! Je pense que ça fait vraiment mouche et c'est propre ! La seule meilleure solution que je puisse imaginer est que Google sorte une WebView qui fonctionne correctement et qu'on n'y touche plus jamais. C'est vraiment décourageant pour les développeurs indépendants comme moi de voir les applications cassées tous les deux dimanches juste parce que. Et, vous savez, préparez-vous à l'ÉNORME changement dans la prochaine version d'Android, ils nous menacent d'un changement TOTAL de WebView basé sur Chromium ... Et dans mon expérience Chrome sur Android est plus lent que le webview stock...

3 votes

La consommation de la batterie est due au fait que la vue est continuellement redessinée. Cette solution atténue complètement ce problème. Je suggère fortement de NE PAS utiliser la réponse choisie pour cette raison.

0 votes

@VicVu J'ai modifié cette réponse pour qu'elle corresponde à la réponse sélectionnée au lieu de la mienne.

8voto

Andrew Bullock Points 14899

Re : le problème de redécoupage, vous pouvez forcer un redécoupage en lisant une propriété de l'élément

alors dis que tu fais ça :

$('#myElement').addClass('foo'); // youre not seeing this take effect

si vous le faites par la suite :

$('#myElement').width();

cela forcera un redessin.

De cette façon, vous pouvez être sélectif au lieu de redessiner la page entière tout le temps, ce qui est coûteux.

0 votes

Je suis vraiment content d'être tombé sur cette réponse, les autres solutions ont vidé ma batterie sans raison, même en limitant les redessinages par seconde. Cette réponse devrait être en haut de la liste !

8voto

Pour moi, ce problème ne se produisait que sur les appareils Samsung. J'ai pu le résoudre en désactivant l'accélération matérielle pour les WebViews :

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

J'espère que cela vous aidera.

0 votes

Merci, cela fonctionne. J'utilise le canvas dans le webview de Cordova.

6voto

Dori Points 4011

Comme nous l'avons souligné plus haut et ailleurs, le fait de passer outre View.onDraw() et en appelant View.invalidate rendra la batterie malheureuse et les performances des applications diminueront. Vous pouvez aussi faire une invalidate appeler ever x ms comme suit

/**
 * Due to bug in 4.2.2 sometimes the webView will not draw its contents after the data has loaded. 
 * Triggers redraw. Does not work in webView's onPageFinished callback for some reason
 */
private void forceWebViewRedraw()
{
    mWebView.post(new Runnable() {
        @Override
        public void run()
        {
            mWebView.invalidate();
            if(!isFinishing())
                mWebView.postDelayed(this, 1000);
        }
    });
}

J'ai essayé de mettre un appel à l'invalidation dans WebViewClient.onPageLoaded() mais cela ne semble pas fonctionner. Bien que ma solution puisse être meilleure, elle est simple et fonctionne pour moi (je montre juste une connexion Twitter).

0 votes

Où avez-vous appelé la méthode forceWebViewRedraw() exactement ?

0 votes

Vous pourriez l'appeler dans le onPageFinished callback. Le commentaire de la méthode fait référence au fait que l'on ne peut pas appeler mWebView.invalidate(); en onPageFinished

0 votes

Quel est le rôle de "isFinishing()" ici ? @Dori

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X