Android WebView rend blank / white, la vue ne se met pas à jour sur les modifications css ou HTML, les animations sont hachées

quand je fais un changement de 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 bas à un bouton. Le bouton ne met pas à jour, ni rien d'autre sur la page. C'est très inconstant quand ça marche. J'ai aussi remarqué que parfois mes éléments apparaissent blancs sans contenu ou quoi que ce soit. C'est très frustrant!

50
demandé sur Kyle 2012-11-21 23:14:56

8 réponses

j'ai mis en œuvre la solution de kyle et ça a résolu le problème. Cependant, j'ai remarqué un énorme drain de batterie sur android 4.0.4 lorsque l'application était ouverte. Aussi après le changement, j'ai eu des utilisateurs se plaignant que le clavier swiftKey ne fonctionnait plus avec mon application.

chaque changement dans mon application est déclenché par une action de l'utilisateur et 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();
        }
    };

n'a Jamais fait de programmation avec Java donc il pourrait y avoir une meilleure solution pour faire cela.

17
répondu Olivier 2013-04-23 16:17:18

Note:

Il ya une meilleure solution à partir de Android 4.4+. Il s'agit d'un remplacement WebView appelé CrossWalk . Il utilise le dernier Chromium-kit et il est fantastique. Vous pouvez le lire ici: crosswalk-project.org

aussi, il apparaît que depuis Android 4.4, la solution invalidate() n'est plus nécessaire et vous pouvez vous en tirer en utilisant certains des autres plus sûr de la réponse. Je n'utiliserais cette approche invalidate() qu'en dernier recours.


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

j'ai essayé plusieurs méthodes pour améliorer les choses, même le tout célèbre - webkit-transform: translate3d(0,0,0); même cela n'a pas trop bien fonctionné.

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 l'option l'accélération matérielle . Si votre version de L'API ne supporte pas cela, passez à la prochaine étape.

si votre version le supporte, Vous pouvez l'activer en modifiant votre manifeste:

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

de plus, assurez-vous que votre manifeste possède l'API minimale supportée par celle que vous utilisez. Depuis que J'utilise API 15, ceci est ce que mon manifeste a:

<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 primaire pour ce qui sera présenté dans un WebView, ajoutez quelque chose comme ceci:

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

ajouter sur le bit body div avec tout autre type d'élément que vous avez; vous pouvez probablement exclure les images, ul , li , etc. La raison d'appliquer ce style CSS à tout est juste par essai et erreur, et j'ai trouvé que brute-l'application à tout semble fonctionner le mieux. Avec un plus grand DOM, vous pourriez avoir besoin d'être plus spécifique. Je ne suis pas sûr de ce que serait la spécification, cependant.

lorsque vous instanciez votre WebView , il y a quelques paramètres que vous voulez paramétrer:

@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-dernier, mais passage crucial: je lisais le code source pour la classe WebView et trouvé ce petit commentaire minuscule sur force redessiner. Il y a un static final boolean , qui lorsqu'il est mis à true forcera la vue à toujours se redessiner. Je ne suis pas énorme sur la syntaxe Java, mais je ne pense pas que vous pouvez changer directement un attribut final statique d'une classe. Alors ce que j'ai fini par faire, c'est que j'ai prolongé la classe comme ça:

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, J'utilise Cordova/PhoneGap, donc j'ai dû prolonger à partir du CordovaWebView . Si vous voyez dans le méthode onDraw, il y a un appel à invalidate . La vue sera alors constamment redessinée. Je recommande fortement d'ajouter de la logique à seulement redessiner quand vous en avez besoin, cependant.

il y a une dernière étape, si vous utilisez Cordova. Vous devez dire à PhoneGap d'utiliser votre nouvelle classe WebView au lieu de leur 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 ça! Essayez et lancez votre application et voir si tout est beaucoup plus lisse. Avant de faire tous ces changements, les pages apparaîtraient blanches, aucun changement CSS ne serait appliqué jusqu'à ce qu'après avoir tapé sur l'écran à nouveau, les animations étaient super choppy ou même pas perceptible. Je dois mentionner que les animations sont toujours instable, mais beaucoup moins agitée qu'auparavant.

Si quelqu'un a quelque chose à ajouter à cela, il suffit de commenter sous. 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 faire. recommandé.

si ma solution ci-dessus ne fonctionnait pas pour vous, pourriez-vous décrire votre situation spécifique et quels résultats vous voyez avec les androïdes WebView ?

enfin, j'ai permis cette réponse comme un " wiki communautaire " , pour que tout le monde se sente libre de faire des ajustements.

Merci!


modifier:

avec le plus récent PhoneGap, vous aurez besoin d'avoir votre méthode init() ressemble plus à ceci:

public void init(){
    CordovaWebView webView = new MyWebView(MainActivity.this);
    super.init(webView, new IceCreamCordovaWebViewClient(this, webView), new CordovaChromeClient(this, webView));
}
58
répondu Kyle 2016-07-28 16:22:38

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

donc, dire que vous faites cela:

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

si vous faites cela après:

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

il va forcer un dessin.

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

8
répondu Andrew Bullock 2013-05-22 07:53:35

pour moi ce problème ne se passait que sur les appareils Samsung. J'ai pu le corriger en désactivant L'accélération matérielle pour WebViews:

webView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);

J'espère que ça aidera.

8
répondu Leandro Glossman 2014-10-09 16:53:02

comme indiqué ci - dessus et ailleurs-l'annulation View.onDraw() et l'appel View.invalidate fera pour une batterie malheureuse / les performances de l'application vont baisser. Vous pouvez aussi faire un manuel invalidate appeler tous les X ms comme ça

/**
 * 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 invalider appeler WebViewClient.onPageLoaded() mais cela ne semble pas fonctionner. Alors que ma solution pourrait être mieux son simple et il fonctionne pour moi (im montrant juste un login twitter)

6
répondu Dori 2013-10-08 16:47:06

s'il vous plaît voir aussi ma réponse à WebView ne rend pas tant que touché Android 4.2.2 , l'idée est d'exporter la fonction de @Olivier à JavaScript de sorte que vous pouvez déclencher invalides DE JS dans les parties sensibles ... Mon dieu, encore un autre plus hack !! :)

0
répondu rupps 2017-05-23 10:29:49

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

depuis s'injectait js faire mes menus et des trucs du site original et a remarqué que l'injection JS sur onPageFinished forcer le webview à dessiner des contenus. Voici un exemple de ce que vous pouvez ajouter à la fin de onPageFinished

view.loadUrl("javascript:(function() { var select = document.getElementsByClassName('something')[0]\r\n" + 
                            "                     if(select)" +
                            "                       select.style.display = 'none';})()");
0
répondu user3353754 2014-02-26 00:47:08

il suffit de vider le cache de webView dans oncreate() et cela fonctionne pour moi.

webView.clearCache(true);
0
répondu Ankush Chugh 2017-12-15 11:28:46