Android WebView rend vide / blanc, la vue ne se met pas à jour sur les modifications CSS ou les modifications HTML, les animations sont instables

Quand je fais un changement de classe CSS, les modifications n’apparaissent pas toujours. Cela semble se produire lorsque j’ai un événement tactile qui ajoute quelque chose comme un nom de classe à un bouton. Le bouton ne se met pas à jour et aucune autre chose ne figure sur la page. C’est très inconstant quand ça marche. J’ai aussi remarqué que parfois, mes éléments apparaissent blancs sans contenu ni rien. C’est très frustrant!

J’ai implémenté la solution de kyle et cela a résolu le problème. Cependant, j’ai remarqué une énorme décharge de batterie sur Android 4.0.4 lorsque l’application était ouverte. Après le changement, des utilisateurs se sont plaints que le clavier swiftKey ne fonctionnait plus avec mon application.

Chaque modification de mon application est déclenchée par une action de l’utilisateur, j’ai donc proposé une version modifiée qui déclenche uniquement une invalidation () après un événement tactile:

Handler handler = new Handler(); public boolean onTouchEvent (MotionEvent event){ super.onTouchEvent(event); handler.postDelayed(sortingggerInvalidate, 60); handler.postDelayed(sortingggerInvalidate, 300); return true; } private Runnable sortingggerInvalidate=new Runnable(){ public void run(){ invalidate(); } }; 

Jamais fait de programmation avec Java, donc il pourrait y avoir une meilleure solution pour ce faire.

Remarque:
Il y a une meilleure solution à partir d’Android 4.4+. Il s’agit d’un remplaçant de WebView appelé CrossWalk . Il utilise le dernier kit Chromium et c’est fantastique. Vous pouvez lire à ce sujet ici: crosswalk-project.org

En outre, il semble que, depuis Android 4.4, la solution invalidate() n’est plus nécessaire et que vous pouvez utiliser une autre solution plus sûre. Je n’utiliserais cette approche d’ invalidate() que comme un ultime effort.


Je réponds à ma propre question pour aider les gens à sortir avec les mêmes problèmes que moi.

J’ai essayé plusieurs méthodes pour améliorer les choses, même les plus notoires: webkit-transform: translate3d(0,0,0); Même cela n’a pas bien fonctionné.

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

Tout d’abord, utilisez l’API la plus récente. J’utilise API 15. Dans votre AndroidManifest.xml , veillez à activer l’accélération matérielle . Si votre version de l’API ne le prend pas en charge, passez au bit suivant.

Si votre version le supporte, vous pouvez l’activer en modifiant votre manifeste:

  

Assurez-vous également que votre manifeste dispose de l’API minimum prise en charge par rapport à celle que vous utilisez. Étant donné que j’utilise API 15, voici ce que mon manifeste a:

  

( 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); } 

Ajouter sur le bit div du corps avec tout autre type d’élément que vous avez; vous pouvez probablement exclure des images, ul , li , etc. La raison de l’application de ce style CSS à tout est juste par essais et erreurs, et j’ai trouvé que l’application brute à tout 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 ce que serait la spécification, cependant.

Lorsque vous instanciez votre WebView , vous devez définir certains parameters:

 @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); } 

Le dernier, mais essentiel: je lisais le code source de la classe WebView et j’ai trouvé ce petit commentaire sur la redéfinition de la force. Il y a un static final boolean , qui, une fois défini sur true , forcera la vue à toujours être redessinée. Je ne suis pas énorme sur la syntaxe Java, mais je ne pense pas que vous pouvez directement modifier un atsortingbut final statique d’une classe. Donc ce que j’ai fini par faire, c’est que j’ai prolongé le cours comme ça:

 import org.apache.cordova.CordovaWebView; import android.content.Context; import android.graphics.Canvas; public class MyWebView extends CordovaWebView { public static final Ssortingng 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, alors j’ai dû étendre depuis CordovaWebView . Si vous voyez dans la méthode onDraw, il y a un appel à invalidate . Cela fera que la vue sera constamment redessinée. Je recommande fortement d’append une logique pour ne redessiner que lorsque vous en avez besoin.

Il y a une dernière étape, si vous utilisez Cordova. Vous devez dire à PhoneGap d’utiliser votre nouvelle classe WebView au lieu de sa propre classe WebView . Dans votre classe 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 de lancer votre application et voyez si tout est plus fluide. Avant de faire toutes ces modifications, les pages sembleraient blanches, aucune modification CSS ne serait appliquée avant d’avoir tapoté à nouveau sur l’écran, les animations étaient très agitées ou même pas perceptibles. Je devrais mentionner que les animations sont encore agitées, mais beaucoup moins agitées qu’auparavant.

Si quelqu’un a quelque chose à append à cela, il suffit de commenter sous. Je suis toujours ouvert aux optimisations; et je suis pleinement conscient qu’il y a peut-être 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 quels résultats voyez-vous avec Androids WebView ?

Enfin, j’ai activé cette réponse sous la forme d’un “wiki de communauté” , afin que tout le monde et tous puissent faire des ajustements.

Merci!


Modifier:

Avec la dernière version de PhoneGap, vous devez avoir votre méthode init () comme ceci:

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

re: le problème de redessin, vous pouvez forcer un redraw en lisant une propriété de l’élément

alors dis tu fais ça:

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

si vous le faites ensuite:

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

ça va forcer un redessiner.

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

Pour moi, ce problème ne se produisait que sur les appareils Samsung. J’ai pu résoudre ce problème en désactivant Hardware Acceleration for WebViews:

 webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null); 

J’espère que cela aide.

Comme indiqué ci-dessus et ailleurs – la View.onDraw() et l’appel de View.invalidate entraîneront une baisse des performances de la batterie / application. Vous pouvez également faire un appel d’ invalidate manuelle x ms comme si

 /** * 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 invalide dans WebViewClient.onPageLoaded() mais cela ne semble pas fonctionner. Bien que ma solution puisse être plus simple, cela fonctionne pour moi (je montre juste un login twitter)

S’il vous plaît voir aussi ma réponse à WebView ne parvient pas à rendre jusqu’à touch Android 4.2.2 , L’idée est d’exporter la fonction de @ Olivier en JavaScript afin que vous puissiez déclencher des invalidations de JS dans les parties sensibles … Mon dieu, encore un autre hack !! 🙂

J’ai eu ce problème parce que onPageStared et onPageFinished je montre et cache une animation de chargement.

Depuis que JS a injecté, fais mes menus et d’autres choses sur le site Web original et remarque que l’injection de JS sur onPageFinished oblige la vue Web à dessiner le contenu. Voici un exemple de ce que vous pouvez append à la fin de onPageFinished

 view.loadUrl("javascript:(function() { var select = document.getElementsByClassName('something')[0]\r\n" + " if(select)" + " select.style.display = 'none';})()"); 

Effacez le cache de webView dans oncreate () et ça marche pour moi.

 webView.clearCache(true);