487 votes

Comment écouter la fin du chargement d'une URL par un WebView ?

J'ai un WebView qui charge une page depuis l'Internet. Je veux montrer une ProgressBar jusqu'à ce que le chargement soit terminé.

Comment puis-je écouter l'achèvement du chargement de la page d'une WebView ?

1 votes

Veuillez considérer que ma réponse est correcte, car elle résout certains problèmes que celle de Ian ne résout pas.

0 votes

Je pense qu'il y a un meilleur moyen d'appeler le code java natif avec des js lorsque la page est chargée. Référez-vous à cette réponse, bien que ce soit pour ios, mais l'idée s'applique ici aussi. .

0 votes

Consultez ma réponse ci-dessous pour une solution minimale et concise.

768voto

ian Points 6471

Étendre le site WebViewClient et appeler onPageFinished () comme suit :

mWebView.setWebViewClient(new WebViewClient() {

   public void onPageFinished(WebView view, String url) {
        // do your stuff here
    }
});

0 votes

J'ai quelques problèmes avec les chargements externes lorsque l'Internet n'est pas très rapide. Par exemple Google Analytics ou d'autres choses de ce genre. J'ai essayé quelques solutions de contournement et cela s'est beaucoup amélioré, mais ce n'est pas encore parfait. Parfois, l'application se fige sur l'écran de chargement et il est impossible de la quitter, à moins de déconnecter le réseau ou de se connecter à un autre bon réseau. Je ne sais pas encore comment résoudre ce problème, mais j'essaie.

0 votes

@Felipe pourquoi n'utilisez-vous pas quelque chose comme ça : stackoverflow.com/questions/4302912/Android-webview-timeout

1 votes

Cela fonctionne parfaitement. Vous pouvez utiliser mWebView.setVisibility(View.INVISIBLE) avant le chargement. Une fois le chargement terminé, vous pouvez le remettre à View.VISIBLE. Je l'ai utilisé avec ce splash screen : Android-developers.blogspot.de/2009/03/

161voto

NeTeInStEiN Points 7331

@ian ce n'est pas précis à 100%. Si vous avez plusieurs iframes dans une page, vous aurez plusieurs onPageFinished (et onPageStarted). Et si vous avez plusieurs redirections, cela peut aussi échouer. Cette approche résout (presque) tous les problèmes :

boolean loadingFinished = true;
boolean redirect = false;

mWebView.setWebViewClient(new WebViewClient() {

    @Override
    public boolean shouldOverrideUrlLoading(WebView view, String urlNewString) {
        if (!loadingFinished) {
            redirect = true;
        }

        loadingFinished = false;
        webView.loadUrl(urlNewString);
        return true;
    }

    @Override
    public void onPageStarted(WebView view, String url) {
        loadingFinished = false;
        //SHOW LOADING IF IT ISNT ALREADY VISIBLE  
    }

    @Override
    public void onPageFinished(WebView view, String url) {
        if (!redirect) {
           loadingFinished = true;
            //HIDE LOADING IT HAS FINISHED
        } else {
            redirect = false; 
        }
    }
});

UPDATE :

Selon la documentation : onPageStarted ne sera PAS appelé lorsque le contenu d'un cadre intégré change, c'est-à-dire en cliquant sur un lien dont la cible est un iframe.

J'ai trouvé un cas spécifique de ce genre sur Twitter où seule une pageFinished était appelée, ce qui a un peu perturbé la logique. Pour résoudre ce problème, j'ai ajouté une tâche planifiée pour supprimer le chargement après X secondes. Ce n'est pas nécessaire dans tous les autres cas.

MISE À JOUR 2 :

Maintenant, avec l'implémentation actuelle de WebView Android :

boolean loadingFinished = true;
boolean redirect = false;

    mWebView.setWebViewClient(new WebViewClient() {

        @Override
        public boolean shouldOverrideUrlLoading(
                WebView view, WebResourceRequest request) {
            if (!loadingFinished) {
               redirect = true;
            }

            loadingFinished = false;
            webView.loadUrl(request.getUrl().toString());
            return true;
        }

        @Override
        public void onPageStarted(
                WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            loadingFinished = false;
            //SHOW LOADING IF IT ISNT ALREADY VISIBLE  
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            if (!redirect) {
               loadingFinished = true;
                //HIDE LOADING IT HAS FINISHED
            } else {
                redirect = false; 
            }
        }
    });

3 votes

Vous avez probablement remarqué l'avertissement dans la doc de onPageStarted : onPageStarted va PAS soit appelé lorsque le contenu d'un cadre intégré change, c'est-à-dire en cliquant sur un lien dont la cible est un iframe. Cela peut donc fausser la logique dans ces cas-là. BTW, la simplification de @polen fonctionne-t-elle vraiment ?

1 votes

Jolie prise, je n'avais jamais remarqué le truc du cadre. Je n'ai pas essayé le code de Polen. Le seul que je puisse assurer qui fonctionne, c'est le code ci-dessus que j'utilise.

3 votes

Juste une petite correction. C'est en fait "public void onPageStarted (WebView view, String url, Bitmap favicon)" au lieu de "public void onPageStarted(WebView view, String url)".

41voto

SciSpear Points 1254

J'ai un faible pour la solution de @NeTeInStEiN (et @polen), mais je l'aurais implémentée avec un compteur au lieu de multiples booléens ou d'observateurs d'état (juste une autre saveur, mais j'ai pensé que je pourrais partager). Il y a une nuance JS à ce sujet, mais je pense que la logique est un peu plus facile à comprendre.

private void setupWebViewClient() {
    webView.setWebViewClient(new WebViewClient() {
        private int running = 0; // Could be public if you want a timer to check.

        @Override
        public boolean shouldOverrideUrlLoading(WebView webView, String urlNewString) {
            running++;
            webView.loadUrl(urlNewString);
            return true;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            running = Math.max(running, 1); // First request move it to 1.
        }

        @Override
        public void onPageFinished(WebView view, String url) {
            if(--running == 0) { // just "running--;" if you add a timer.
                // TODO: finished... if you want to fire a method.
            }
        }
    });
}

2 votes

Votre code semble bon. J'ai essayé la même chose pour ma webview qui se charge à partir de données html et non d'url. Mais celui-ci déclenche le onPageFinished si la première webview se charge rapidement et avant que la seconde ne commence à se charger. Scénario normal. running =1 , running = 2, --running =1 , --running =0 Scénario inhabituel qui échoue - running =1 , --running = 0, running =1 running =0

1 votes

Il existe également un autre scénario : si un événement n'est pas déclenché pour une raison quelconque, la partie TODO ne sera jamais atteinte. Vous avez donc besoin d'une minuterie pour vérifier les délais d'attente. Ou vous pouvez appeler une fonction java exportée en javascript lorsque vous savez que tout est chargé correctement. Quelque chose comme documentReady() ou quelque chose comme ça.

0 votes

Pourquoi ne pas simplement running = 1; sur onPageStarted() ?

24voto

Francesco Laurita Points 12027

Si vous voulez afficher une barre de progression, vous devez écouter un événement de changement de progression, et pas seulement l'achèvement de la page :

mWebView.setWebChromeClient(new WebChromeClient(){

            @Override
            public void onProgressChanged(WebView view, int newProgress) {

                //change your progress bar
            }

        });

Si vous souhaitez afficher une barre de progression indéterminée, il suffit de remplacer la méthode onPageFinished.

23voto

polen Points 241

J'ai simplifié NeTeInStEiN Le code de l'entreprise doit être comme ça :

mWebView.setWebViewClient(new WebViewClient() {
        private int       webViewPreviousState;
        private final int PAGE_STARTED    = 0x1;
        private final int PAGE_REDIRECTED = 0x2;

        @Override
        public boolean shouldOverrideUrlLoading(WebView view, String urlNewString) {
            webViewPreviousState = PAGE_REDIRECTED;
            mWebView.loadUrl(urlNewString);
            return true;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            super.onPageStarted(view, url, favicon);
            webViewPreviousState = PAGE_STARTED;
            if (dialog == null || !dialog.isShowing())
                dialog = ProgressDialog.show(WebViewActivity.this, "", getString(R.string.loadingMessege), true, true,
                        new OnCancelListener() {

                            @Override
                            public void onCancel(DialogInterface dialog) {
                                // do something
                            }
                        });
        }

        @Override
        public void onPageFinished(WebView view, String url) {

            if (webViewPreviousState == PAGE_STARTED) {
                dialog.dismiss();
                dialog = null;
            }

        }
 });

C'est facile à comprendre, OnPageFinished si le callback précédent est sur onPageStarted, donc la page est complètement chargée.

3 votes

@polen PAGE_STARTED n'est jamais effacé ? Et si shouldOverrideUrlLoading() n'est jamais appelé ?

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