How to listen for a WebView finishing loading a URL?

前端 未结 17 732
予麋鹿
予麋鹿 2020-11-22 05:40

I have a WebView that is loading a page from the Internet. I want to show a ProgressBar until the loading is complete.

How do I listen for

相关标签:
17条回答
  • 2020-11-22 06:11

    The renderer will not finish rendering when the OnPageFinshed method is called or the progress reaches 100% so both methods don't guarantee you that the view was completely rendered.

    But you can figure out from OnLoadResource method what has been already rendered and what is still rendering. And this method gets called several times.

            @Override
            public void onLoadResource(WebView view, String url) {
                super.onLoadResource(view, url);
               // Log and see all the urls and know exactly what is being rendered and visible. If you wanna know when the entire page is completely rendered, find the last url from log and check it with if clause and implement your logic there.
                if (url.contains("assets/loginpage/img/ui/forms/")) {
                    // loginpage is rendered and visible now.
                   // your logic here.
    
                }
            }
    
    0 讨论(0)
  • 2020-11-22 06:12

    Loading url with SwipeRefreshLayout and ProgressBar:

    UrlPageActivity.java:

        WebView webView;
        SwipeRefreshLayout _swipe_procesbar;
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_url_page);
    
    
            String url = "http://stackoverflow.com/";
    
            _swipe_procesbar = (SwipeRefreshLayout)findViewById(R.id.url_path_swipe_procesbar);
    
            _swipe_procesbar.post(new Runnable() {
                                      @Override
                                      public void run() {
                                          _swipe_procesbar.setRefreshing(true);
                                      }
                                  }
            );
    
            webView = (WebView) findViewById(R.id.url_page_web_view);
            webView.getSettings().setJavaScriptEnabled(true);
    
            webView.setWebViewClient(new WebViewClient() {
                public void onPageFinished(WebView view, String url) {
                    _swipe_procesbar.setRefreshing(false);
                    _swipe_procesbar.setEnabled(false);
                }
            });
            webView.loadUrl(url);
        }
    

    activity_url_page.xml:

    <android.support.v4.widget.SwipeRefreshLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/url_path_swipe_procesbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
            <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
                android:layout_height="match_parent"
                tools:context="com.test.test1.UrlPageActivity">
    
    
                <WebView
                    android:layout_width="fill_parent"
                    android:layout_height="fill_parent"
                    android:id="@+id/url_page_web_view" />
            </RelativeLayout>
    
    </android.support.v4.widget.SwipeRefreshLayout>
    
    0 讨论(0)
  • 2020-11-22 06:17

    @ian this is not 100% accurate. If you have several iframes in a page you will have multiple onPageFinished (and onPageStarted). And if you have several redirects it may also fail. This approach solves (almost) all the problems:

    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:

    According to the documentation: onPageStarted will NOT be called when the contents of an embedded frame changes, i.e. clicking a link whose target is an iframe.

    I found a specific case like that on Twitter where only a pageFinished was called and messed the logic a bit. To solve that I added a scheduled task to remove loading after X seconds. This is not needed in all the other cases.

    UPDATE 2:

    Now with current Android WebView implementation:

    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; 
                }
            }
        });
    
    0 讨论(0)
  • 2020-11-22 06:19

    Here's a novel method for detected when a URL has loaded by utilising Android's capability for JavaScript hooks. Using this pattern, we exploit JavaScript's knowledge of the document's state to generate a native method call within the Android runtime. These JavaScript-accessible calls can be made using the @JavaScriptInterface annotation.

    This implementation requires that we call setJavaScriptEnabled(true) on the WebView's settings, so it might not be suitable depending on your application's requirements, e.g. security concerns.

    src/io/github/cawfree/webviewcallback/MainActivity.java (Jelly Bean, API Level 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"
    />
    

    Essentially, we're appending an additional JavaScript function that is used to test the state of the document. If it's loaded, we launch a custom onPageLoaded() event in Android's MainActivity; otherwise, we register an event listener that updates Android once the page is ready, using window.addEventListener('onload', ...);.

    Since we're appending this script after the call to this.getWebView().loadURL("") has been made, it's probable that we don't need to 'listen' for the events at all, since we only get a chance to append the JavaScript hook by making a successive call to loadURL, once the page has already been loaded.

    0 讨论(0)
  • 2020-11-22 06:19

    Use this it should help.`var currentUrl = "google.com" var partOfUrl = currentUrl.substring(0, currentUrl.length-2)

    webView.setWebViewClient(object: WebViewClient() {

    override fun onLoadResource(WebView view, String url) {
         //call loadUrl() method  here 
         // also check if url contains partOfUrl, if not load it differently.
         if(url.contains(partOfUrl, true)) {
             //it should work if you reach inside this if scope.
         } else if(!(currentUrl.startWith("w", true))) {
             webView.loadurl("www.$currentUrl")
    
         } else if(!(currentUrl.startWith("h", true))) {
             webView.loadurl("https://$currentUrl")
    
         } else { ...}
    
    
     }
    
    override fun onReceivedSslError(view: WebView?, handler: SslErrorHandler?, error: SslError?) {
       // you can call again loadUrl from here too if there is any error.
    }
    

    //You should also override other override method for error such as onReceiveError to see how all these methods are called one after another and how they behave while debugging with break point. } `

    0 讨论(0)
  • 2020-11-22 06:21

    If you want show a progress bar you need to listen for a progress change event, not just for the completion of page:

    mWebView.setWebChromeClient(new WebChromeClient(){
    
                @Override
                public void onProgressChanged(WebView view, int newProgress) {
    
                    //change your progress bar
                }
    
    
            });
    

    BTW if you want display just an Indeterminate ProgressBar overriding the method onPageFinished is enough

    0 讨论(0)
提交回复
热议问题