Как прослушать WebView, заканчивающий загрузку URL? - PullRequest
411 голосов
/ 30 июня 2010

У меня есть WebView, который загружает страницу из Интернета. Я хочу показать ProgressBar, пока загрузка не будет завершена.

Как мне прослушать завершение загрузки страницы WebView?

Ответы [ 15 ]

669 голосов
/ 30 июня 2010

Расширить WebViewClient и вызвать onPageFinished () следующим образом:

mWebView.setWebViewClient(new WebViewClient() {

   public void onPageFinished(WebView view, String url) {
        // do your stuff here
    }
});
142 голосов
/ 02 марта 2011

@ ian это не на 100% точно. Если у вас есть несколько фреймов на странице, у вас будет несколько onPageFinished (и onPageStarted). И если у вас есть несколько перенаправлений, это может также потерпеть неудачу. Этот подход решает (почти) все проблемы:

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:

Согласно документации: onPageStarted НЕ будет вызываться при изменении содержимого встроенного фрейма, т. е. по ссылке, целью которой является iframe.

Я нашел конкретный случай, подобный этому, в Твиттере, где вызывался только pageFinished, и немного ломал логику. Чтобы решить эту проблему, я добавил запланированное задание для удаления загрузки через X секунд. Это не нужно во всех остальных случаях.

ОБНОВЛЕНИЕ 2 :

Теперь с текущей реализацией Android WebView:

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; 
            }
        }
    });
39 голосов
/ 23 июля 2013

Я довольно неравнодушен к решению @NeTeInStEiN (и @polen), но реализовал бы его со счетчиком, а не с несколькими логическими значениями или наблюдателями состояния (просто другой вариант, но я подумал, что может поделиться) У него есть нюанс JS, но я чувствую, что логику немного легче понять.

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.
            }
        }
    });
}
23 голосов
/ 05 августа 2011

Я упростил код NeTeInStEiN , чтобы он был таким:

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

        }
 });

Это легко понять, OnPageFinished, если предыдущий обратный вызов включен onPageStarted, поэтому страница полностью загружена.

16 голосов
/ 13 октября 2015

Я также нашел одно элегантное решение, но я не проверял его строго:

public void onPageFinished(WebView view, String url) {
            super.onPageFinished(view, url);
            if (m_webView.getProgress() == 100) {
                progressBar.setVisibility(View.GONE);
                m_webView.setVisibility(View.VISIBLE);
            }
        } 
16 голосов
/ 30 июня 2010

Если вы хотите показать индикатор выполнения, вам нужно прослушивать событие изменения прогресса, а не только для завершения страницы:

mWebView.setWebChromeClient(new WebChromeClient(){

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

                //change your progress bar
            }


        });

Кстати, если вы хотите отобразить только Indeterminate ProgressBar, переопределяющий метод onPageFinishedдостаточно

6 голосов
/ 30 июня 2010

Использовать setWebViewClient () и переопределять onPageFinished ()

5 голосов
/ 19 мая 2015

спасибо за ответы. Это помогло мне, но мне пришлось немного улучшить его для своих нужд. У меня было несколько запусков страниц и завершений, поэтому я добавил таймер, который проверяет, запускается ли при завершении страницы новый запуск страницы. Хорошо, плохое объяснение. Смотрите код:)

myWebView.setWebViewClient(new WebViewClient() {
        boolean loadingFinished = true;
        boolean redirect = false;

        long last_page_start;
        long now;

        // Load the url
        public boolean shouldOverrideUrlLoading(WebView view, String url) {
            if (!loadingFinished) {
                redirect = true;
            }

            loadingFinished = false;
            view.loadUrl(url);
            return false;
        }

        @Override
        public void onPageStarted(WebView view, String url, Bitmap favicon) {
            Log.i("p","pagestart");
            loadingFinished = false;
            last_page_start = System.nanoTime();
            show_splash();
        }

        // When finish loading page
        public void onPageFinished(WebView view, String url) {
            Log.i("p","pagefinish");
            if(!redirect){
                loadingFinished = true;
            }
            //call remove_splash in 500 miSec
            if(loadingFinished && !redirect){
                now = System.nanoTime();
                new android.os.Handler().postDelayed(
                        new Runnable() {
                            public void run() {
                                remove_splash();
                            }
                        },
                        500);
            } else{
                redirect = false;
            }
        }
        private void show_splash() {
            if(myWebView.getVisibility() == View.VISIBLE) {
                myWebView.setVisibility(View.GONE);
                myWebView_splash.setVisibility(View.VISIBLE);
            }
        }
        //if a new "page start" was fired dont remove splash screen
        private void remove_splash() {
            if (last_page_start < now) {
                myWebView.setVisibility(View.VISIBLE);
                myWebView_splash.setVisibility(View.GONE);
            }
        }

});
5 голосов
/ 30 июня 2010

Вы можете отследить статус прогресса с помощью метода getProgress в webview class.

Инициализировать статус прогресса

private int mProgressStatus = 0;

тогда AsyncTask для загрузки выглядит так:

private class Task_News_ArticleView extends AsyncTask<Void, Void, Void> {
    private final ProgressDialog dialog = new ProgressDialog(
            your_class.this);

    // can use UI thread here
    protected void onPreExecute() {
        this.dialog.setMessage("Loading...");
        this.dialog.setCancelable(false);
        this.dialog.show();
    }

    @Override
    protected Void doInBackground(Void... params) {
        try {
            while (mProgressStatus < 100) {
                mProgressStatus = webview.getProgress();

            }
        } catch (Exception e) {

        }
        return null;

    }

    protected void onPostExecute(Void result) {
        if (this.dialog.isShowing()) {
            this.dialog.dismiss();
        }
    }
}
1 голос
/ 30 марта 2017

Вот новый метод обнаружения, когда URL-адрес загружен, с использованием возможности Android для перехвата JavaScript .Используя этот шаблон, мы используем знания JavaScript о состоянии документа для генерации собственного вызова метода во время выполнения Android.Эти доступные через JavaScript вызовы могут быть сделаны с помощью аннотации @JavaScriptInterface.

Для этой реализации требуется, чтобы мы вызывали setJavaScriptEnabled(true) в настройках WebView, поэтому может не подойти в зависимости от требований вашего приложения, например, из соображений безопасности.

src / io / github / cawfree / webviewcallback / MainActivity.java (Jelly Bean, уровень API 16)

package io.github.cawfree.webviewcallback;

/**
 *  Created by Alex Thomas (@Cawfree), 30/03/2017.
 **/

import android.net.http.SslError;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.webkit.JavascriptInterface;
import android.webkit.SslErrorHandler;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;

/** An Activity demonstrating how to introduce a callback mechanism into Android's WebView. */
public class MainActivity extends AppCompatActivity {

    /* Static Declarations. */
    private static final String HOOK_JS             = "Android";
    private static final String URL_TEST            = "http://www.zonal.co.uk/";
    private static final String URL_PREPARE_WEBVIEW = "";

    /* Member Variables. */
    private WebView mWebView = null;

    /** Create the Activity. */
    @Override protected final void onCreate(final Bundle pSavedInstanceState) {
        // Initialize the parent definition.
        super.onCreate(pSavedInstanceState);
        // Set the Content View.
        this.setContentView(R.layout.activity_main);
        // Fetch the WebView.
        this.mWebView = (WebView)this.findViewById(R.id.webView);
        // Enable JavaScript.
        this.getWebView().getSettings().setJavaScriptEnabled(true);
        // Define the custom WebClient. (Here I'm just suppressing security errors, since older Android devices struggle with TLS.)
        this.getWebView().setWebViewClient(new WebViewClient() { @Override     public final void onReceivedSslError(final WebView pWebView, final SslErrorHandler pSslErrorHandler, final SslError pSslError) { pSslErrorHandler.proceed(); } });
        // Define the WebView JavaScript hook.
        this.getWebView().addJavascriptInterface(this, MainActivity.HOOK_JS);
        // Make this initial call to prepare JavaScript execution.
        this.getWebView().loadUrl(MainActivity.URL_PREPARE_WEBVIEW);
    }

    /** When the Activity is Resumed. */
    @Override protected final void onPostResume() {
        // Handle as usual.
        super.onPostResume();
        // Load the URL as usual.
        this.getWebView().loadUrl(MainActivity.URL_TEST);
        // Use JavaScript to embed a hook to Android's MainActivity. (The onExportPageLoaded() function implements the callback, whilst we add some tests for the state of the WebPage so as to infer when to export the event.)
        this.getWebView().loadUrl("javascript:" + "function onExportPageLoaded() { " + MainActivity.HOOK_JS + ".onPageLoaded(); }" + "if(document.readyState === 'complete') { onExportPageLoaded(); } else { window.addEventListener('onload', function () { onExportPageLoaded(); }, false); }");
    }

    /** Javascript-accessible callback for declaring when a page has loaded. */
    @JavascriptInterface @SuppressWarnings("unused") public final void onPageLoaded() {
        // Display the Message.
        Toast.makeText(this, "Page has loaded!", Toast.LENGTH_SHORT).show();
    }

    /* Getters. */
    public final WebView getWebView() {
        return this.mWebView;
    }

}

res / layout / activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<WebView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/webView"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
/>

По сути, мы добавляем дополнительную функцию JavaScript, которая используется для проверки состояния документа.Если он загружен, мы запускаем пользовательское событие onPageLoaded() в Android MainActivity;в противном случае мы регистрируем прослушиватель событий, который обновляет Android после того, как страница готова, используя window.addEventListener('onload', ...);.

Поскольку мы добавляем этот скрипт после выполнения вызова this.getWebView().loadURL(""), вероятно, мы не будем 'прослушивать' событий вообще не нужно, поскольку у нас есть возможность добавить ловушку JavaScript только путем последовательного вызова loadURL, как только страница уже загружена.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...