首页 文章

如何监听WebView完成加载URL?

提问于
浏览
380

我有 WebView 正在从Internet加载页面 . 我想在加载完成之前显示 ProgressBar .

我如何监听WebView页面加载的完成?

13 回答

  • 38

    扩展WebViewClient并调用onPageFinished(),如下所示:

    mWebView.setWebViewClient(new WebViewClient() {
    
       public void onPageFinished(WebView view, String url) {
            // do your stuff here
        }
    });
    
  • 16

    只是为了显示进度条,“onPageStarted”和“onPageFinished”方法就足够了;但是如果你想要一个“is_loading”标志(以及页面重定向,......),这个方法可以用非顺序执行,比如“onPageStarted> onPageStarted> onPageFinished> onPageFinished”队列 .

    但是通过我的简短测试(自己测试),“onProgressChanged”方法值队列是“0-100> 0-100> 0-100> ......”

    private boolean is_loading = false;
    
    webView.setWebChromeClient(new MyWebChromeClient(context));
    
    private final class MyWebChromeClient extends WebChromeClient{
        @Override
        public void onProgressChanged(WebView view, int newProgress) {
            if (newProgress == 0){
                is_loading = true;
            } else if (newProgress == 100){
                is_loading = false;
            }
            super.onProgressChanged(view, newProgress);
        }
    }
    

    如果活动关闭,则在活动关闭时设置“ is_loading = false ”,因为活动可以在页面完成之前完成 .

  • 22

    我简化了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上,那么页面就完全加载了 .

  • 0

    @ian这不是100%准确 . 如果页面中有多个iframe,则会有多个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;
            }
    
            if (loadingFinished && !redirect) {
                //HIDE LOADING IT HAS FINISHED
            } else {
                redirect = false; 
            }
        }
    });
    

    UPDATE:

    根据文档:当嵌入框架的内容发生变化时,不会调用onPageStarted,即单击目标为iframe的链接 .

    我在Twitter上找到了一个特定的案例,其中只有一个pageFinished被调用并且稍微混淆了逻辑 . 为了解决这个问题,我在X秒后添加了一个计划任务来删除加载 . 在所有其他情况下都不需要这样做 .

    UPDATE 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;
                }
    
                if (loadingFinished && !redirect) {
                    //HIDE LOADING IT HAS FINISHED
                } else {
                    redirect = false; 
                }
            }
        });
    
  • 1

    我找到了一个优雅的解决方案,但没有经过严格的测试:

    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);
                }
            }
    
  • 0

    这是一个允许您注册 Runnable 的方法,以便在特定网址完成加载后执行 . 我们将 RunnableMap 中的相应URL String 相关联,并使用 WebViewgetOriginalUrl() 方法选择适当的回调 .

    package io.github.cawfree.webviewcallback;
    
    /**
     *  Created by Alex Thomas (@Cawfree), 30/03/2017.
     **/
    
    import android.net.http.SslError;
    import android.os.Bundle;
    import android.support.v7.app.AppCompatActivity;
    import android.webkit.SslErrorHandler;
    import android.webkit.WebView;
    import android.webkit.WebViewClient;
    
    import java.util.HashMap;
    import java.util.Map;
    
    /** An Activity demonstrating how to introduce a callback mechanism into Android's WebView. */
    public class MainActivity extends AppCompatActivity {
    
        /* Member Variables. */
        private WebView               mWebView;
        private Map<String, Runnable> mCallbackMap;
    
        /** 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);
            this.mCallbackMap = new HashMap<>();
            // Define the custom WebClient. (Here I'm just suppressing security errors, since older Android devices struggle with TLS.)
            this.getWebView().setWebViewClient(new WebViewClient() {
                /** Handle when a request has been launched. */
                @Override public final void onPageFinished(final WebView pWebView, final String pUrl) {
                    // Determine whether we're allowed to process the Runnable; if the page hadn't been redirected, or if we've finished redirection.
                    if(pUrl.equals(pWebView.getOriginalUrl())) {
                        // Fetch the Runnable for the OriginalUrl.
                        final Runnable lRunnable = getCallbackMap().get(pWebView.getOriginalUrl());
                        // Is it valid?
                        if(lRunnable != null) { lRunnable.run(); }
                    }
                    // Handle as usual.
                    super.onPageFinished(pWebView, pUrl);
                }
                /** Ensure we handle SSL state properly. */
                @Override public final void onReceivedSslError(final WebView pWebView, final SslErrorHandler pSslErrorHandler, final SslError pSslError) { pSslErrorHandler.proceed(); }
            });
            // Assert that we wish to visit Zonal's website.
            this.getWebView().loadUrl("http://www.zonal.co.uk/");
            // Align a Callback for Zonal; this will be serviced once the page has loaded.
            this.getCallbackMap().put("http://www.zonal.co.uk/", new Runnable() {     @Override public void run() { /* Do something. */ } });
        }
    
        /* Getters. */
        public final WebView getWebView() {
            return this.mWebView;
        }
    
        private final Map<String, Runnable> getCallbackMap() {
            return this.mCallbackMap;
        }
    
    }
    
  • 15

    如果要显示进度条,则需要监听进度更改事件,而不仅仅是完成页面:

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

    顺便说一句,如果你想只显示一个Indeterminate ProgressBar,那么覆盖onPageFinished方法就足够了

  • 0
  • 5

    我非常偏爱@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.
                }
            }
        });
    }
    
  • 6

    这是一种利用Android's capability for JavaScript hooks加载URL时检测到的新方法 . 使用此模式,我们利用JavaScript 's knowledge of the document' s状态在Android运行时内生成本机方法调用 . 可以使用 @JavaScriptInterface 注释进行这些JavaScript可访问的调用 .

    此实现要求我们在 WebView 的设置上调用 setJavaScriptEnabled(true) ,因此根据您的应用程序的要求,它可能不合适,例如:安全问题 .

    src/io/github/cawfree/webviewcallback/MainActivity.java (果冻 beans ,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"
    />
    

    基本上,我们加载了're appending an additional JavaScript function that is used to test the state of the document. If it',我们在Android的 MainActivity 中启动自定义 onPageLoaded() 事件;否则,我们注册一个事件监听器,一旦页面准备好,使用 window.addEventListener('onload', ...); 更新Android .

    由于我们在调用 this.getWebView().loadURL("") 之后附加了这个脚本,因此它们根本不需要'listen'来处理事件,因为我们只有通过连续调用 loadURL 才能附加JavaScript钩子 . 已经加载了 .

  • 633

    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>
    
  • 136

    您可以通过webview类中的getProgress方法跟踪Progress Staus .

    初始化进度状态

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

    谢谢你的回答 . 它帮助了我,但我必须根据我的需要改进它 . 我有几个页面和完成,所以我添加了一个计时器,检查是否启动了pagefinish新的pagestart . 好的,不好的解释 . 看代码:)

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

相关问题