web-dev-qa-db-ja.com

URLのロードが完了したWebViewをリッセンする方法

インターネットからページを読み込んでいるWebViewがあります。ロードが完了するまでProgressBarを表示したいです。

WebView のページロードが完了するのをどうやって聞くのですか?

405
Janusz

WebViewClient を拡張して、 onPageFinished ()を次のように呼び出します。

mWebView.setWebViewClient(new WebViewClient() {

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

@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;
            //HIDE LOADING IT HAS FINISHED
        } else {
            redirect = false; 
        }
    }
});

更新:

ドキュメントによれば、onPageStartedは埋め込みフレームの内容が変わったとき、すなわちターゲットがiframeであるリンクをクリックしたときには呼び出されません。

私はそのような特定のケースを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; 
            }
        }
    });
141
neteinstein

私は@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.
            }
        }
    });
}
38
SciSpear

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

        }
 });

前のコールバックがonPageStartedにある場合は、OnPageFinishedで理解しやすいので、ページは完全にロードされます。

22
polen

私は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);
            }
        } 
16
Skynet

プログレスバーを表示したい場合は、ページの完成だけではなく、プログレスチェンジイベントを監視する必要があります。

mWebView.setWebChromeClient(new WebChromeClient(){

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

                //change your progress bar
            }


        });

ところで、onPageFinishedメソッドをオーバーライドして、不確定なProgressBarだけを表示したい場合は十分です。

16
6
Robby Pond

答えてくれてありがとう。それは私を助けました、しかし私は私のニーズのためにそれを少し改善しなければなりませんでした。私はいくつかのページスタートとフィニッシュを持っていたので、ページフィニッシュが新しいページスタートで開始されるかどうかをチェックするタイマーを追加しました。さて、悪い説明。コードを見てください:)

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
EscapeNetscape

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
Praveen

これはURLが利用されることによってロードされたときに検出されるための新しい方法です JavaScriptフックのためのAndroidの能力 。このパターンを使用して、Androidランタイム内でネイティブメソッド呼び出しを生成するために、JavaScriptのドキュメントの状態に関する知識を活用します。これらのJavaScriptからアクセス可能な呼び出しは@JavaScriptInterfaceアノテーションを使って行うことができます。

この実装はWebViewの設定でsetJavaScriptEnabled(true)を呼び出すことを要求します、それであなたのアプリケーションの要求次第でそれは適切でないかもしれませんです。セキュリティ上の問題.

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"
/>

基本的に、ドキュメントの状態をテストするために使用される追加のJavaScript関数を追加しています。それがロードされると、AndroidのMainActivityでカスタムのonPageLoaded()イベントを起動します。そうでなければ、window.addEventListener('onload', ...);を使用して、ページの準備ができたらAndroidを更新するイベントリスナーを登録します。

this.getWebView().loadURL("")の呼び出しが行われた後にこのスクリプトを追加しているので、JavaScriptを追加する機会しか得られないので、イベントに対して'listen'を実行する必要はまったくないと考えられます。ページが既にロードされたら、loadURLを連続して呼び出すことでフックします。

1
Mapsy

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.

            }
        }
0
shreedhar

SwipeRefreshLayoutProgressBarを使用してurlをロードしています:

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
Hamid

kotlinをお使いの方へ:

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

オーバーライドできるメソッドはたくさんありますが

0

これは、特定のWebアドレスのロードが完了したときに実行されるRunnableを登録できるようにするメソッドです。各RunnableString内の対応するURL Mapに関連付け、適切なコールバックを選択するために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;
    }

}
0
Mapsy

プログレスバーを表示するには、 "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"を設定します。