代码之家  ›  专栏  ›  技术社区  ›  Janusz Daniel Rindt

如何监听加载完URL的WebView?

  •  396
  • Janusz Daniel Rindt  · 技术社区  · 15 年前

    WebView ProgressBar 直到装载完成。

    如何侦听一个文件的页面加载完成 WebView ?

    13 回复  |  直到 9 年前
        1
  •  747
  •   Taslim Oseni    7 年前

    延伸 WebViewClient 打电话给我 onPageFinished

    mWebView.setWebViewClient(new WebViewClient() {
    
       public void onPageFinished(WebView view, String url) {
            // do your stuff here
        }
    });
    
        2
  •  157
  •   Community Mohan Dere    7 年前

    @伊恩,这不是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;
                //HIDE LOADING IT HAS FINISHED
            } else {
                redirect = false; 
            }
        }
    });
    

    更新:

    当嵌入帧的内容发生更改时,即单击目标为iframe的链接时,不会调用onPageStarted。

    我在Twitter上发现了这样一个特殊的例子,只调用了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; 
                }
            }
        });
    
        3
  •  40
  •   Ben Pearson    10 年前

    我非常偏爱@NeTeInStEiN(和@polen)的解决方案,但我会用一个计数器来实现它,而不是用多个布尔函数或状态观察器(这只是另一种风格,但我认为可能是相同的)。它确实有一些细微的差别,但我觉得逻辑更容易理解。

    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.
                }
            }
        });
    }
    
        4
  •  30
  •   Junaid    10 年前

    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);
                }
            } 
    
        5
  •  23
  •   Community Mohan Dere    9 年前

    我简化了 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;
                }
    
            }
     });
    

        6
  •  21
  •   Francesco Laurita    15 年前

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

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

    顺便说一句,如果您只想显示一个不确定的ProgressBar,覆盖onPageFinished方法就足够了

        7
  •  6
  •   Robby Pond    15 年前
        8
  •  5
  •   BenMorel Manish Pradhan    12 年前

    你可以通过 getProgress webview

    初始化进度状态

    private int mProgressStatus = 0;
    

    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();
            }
        }
    }
    
        9
  •  5
  •   EscapeNetscape    11 年前

    谢谢你的回答。它帮助了我,但我必须改进它一点,以满足我的需要。我有几个pagestart和finishes,所以我添加了一个计时器来检查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);
                }
            }
    
    });
    
        10
  •  3
  •   Mapsy    9 年前

    下面是一种新的方法,通过利用 Android's capability for JavaScript hooks @JavaScriptInterface 注释。

    setJavaScriptEnabled(true) WebView 可能不合适

    src/io/github/cawfree/webviewcallback/MainActivity.java

    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\u 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 ; 否则,我们注册一个事件监听器,在页面准备好后使用 window.addEventListener('onload', ...); .

    因为我们在调用 this.getWebView().loadURL("") “听着” 对于所有事件,因为我们只有通过连续调用 loadURL ,一旦页面已加载。

        11
  •  2
  •   Jalil Hamdollahi Oskouei    8 年前

    只要显示进度条,“onPageStarted”和“onPageFinished”方法就足够了;但是,如果您想有一个“is\u 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 在活动关闭时,如果它是静态变量,因为活动可以在页面完成之前完成。

        12
  •  2
  •   Amin Keshavarzian    7 年前

    对于Kotlin用户:

    webView.webViewClient = object : WebViewClient() {
                override fun onPageFinished(view: WebView?, url: String?) {
                    // do your logic
                }
            }
    

        13
  •  0
  •   Hamid    9 年前

    正在加载url SwipeRefreshLayout 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);
        }
    

    <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>
    
        14
  •  0
  •   Mapsy    9 年前

    这里有一个方法可以让你注册一个 Runnable 在特定网址加载完毕后执行。我们互相联系 具有相应的URL String 在一个 Map WebView getOriginalUrl() 方法来选择适当的回调。

    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
  •  0
  •   shreedhar    7 年前

    当调用OnPageFinshed方法或进度达到100%时,渲染器将无法完成渲染,因此这两种方法都不能保证视图已完全渲染。

    但您可以从OnLoadResource方法中找出哪些已经呈现,哪些仍在呈现。这个方法被多次调用。

            @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.
    
                }
            }
    
        16
  •  0
  •   SomeOne    6 年前

    webView.setWebViewClient(对象: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.
    }
    

    //您还应该为错误重写其他重写方法,例如onReceiveError,以查看如何逐个调用所有这些方法,以及在使用断点进行调试时它们的行为。 }

        17
  •  0
  •   niek tuytel    5 年前

    这将在他开始加载页面之前调用 (并获得与 onFinished()

    @Override
    public void onPageCommitVisible(WebView view, String url) {
       super.onPageCommitVisible(view, url);
    }
    
        18
  •  0
  •   Cemil AKAN    5 年前

    Kotlin溶液

    第一个解决方案

    class MainActivity : AppCompatActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val webView : WebView = findViewById(R.id.webView)
        webView.webViewClient = MyWebViewClient()
     }
    
    private class MyWebViewClient : WebViewClient() {
        override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
            println("Load Started")
        }
        override fun onPageFinished(view: WebView, url: String) {
            println("Load Finished")
        }
      }
    }
    

    class MainActivity : AppCompatActivity() {
      override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        val webView : WebView = findViewById(R.id.webView)
        webView.webViewClient = object : WebViewClient() {
            override fun onPageStarted(view: WebView?, url: String?, favicon: Bitmap?) {
                println("Load Started")
            }
            override fun onPageFinished(view: WebView, url: String) {
                println("Load Finished")
            }
         }
     }
    

    迟到总比不做好

    推荐文章