天天看點

WebView使用詳解(三)——WebChromeClient

轉自:http://blog.csdn.net/harvic880925/article/details/51583253

一、WebChromeClient

1、概述

(1)、 與WebViewClient的差別

很多同學一看到這裡有Chrome,立馬就會想到google 的Chrome浏覽器;這裡并不是指Chrome浏覽器的意思,而是泛指浏覽器,WebView的内部實作并不是完全使用Chrome的核心,而是部分使用Chome核心,其它都是與Chrome不相同的; 

我們再來對比下WebViewClient:

  • WebViewClient:在影響View的事件到來時,會通過WebViewClient中的方法回調通知使用者
  • WebChromeClient:當影響浏覽器的事件到來時,就會通過WebChromeClient中的方法回調通知用法。

通過上面的對比,我們發現WebViewClient和WebChromeClient都是針對不同僚件的回調,而google将這些回調進行分類集合,就産生了WebViewClient、WebChromeClient這兩個大類,其中管理着針對不同類型的回調而已。

(2)、為什麼叫WebChromeClient?

因為WebChromeClient中集合了影響浏覽器的事件到來時的回調方法,是以這裡更需要突出浏覽器的概念,而Chrome則是google自家的浏覽器名稱,是以使用WebChromeClient來做為名稱吧,純屬臆想……

(3)、WebChromeClient的常用函數

我們先來看一下,在WebChromeClient中我們将要講解的函數,其實WebChromeClient裡的函數是非常多的,可以監控到浏覽器的很多方面,這裡我們就不再一個個來講了,隻講解常用的幾個函數,而且随着ReactNative和Hybird的普及,WebView的使用場景會越來越少,指不定哪一天就被廢棄了

/**
 * 當網頁調用alert()來彈出alert彈出框前回調,用以攔截alert()函數
 */
public boolean onJsAlert(WebView view, String url, String message,JsResult result)
/**
 * 當網頁調用confirm()來彈出confirm彈出框前回調,用以攔截confirm()函數
 */
public boolean onJsConfirm(WebView view, String url, String message,JsResult result)
/**
 * 當網頁調用prompt()來彈出prompt彈出框前回調,用以攔截prompt()函數
 */
 public boolean onJsPrompt(WebView view, String url, String message,String defaultValue, JsPromptResult result) 
 /**
 * 列印 console 資訊
 */
 public boolean onConsoleMessage(ConsoleMessage consoleMessage)
 /**
 * 通知程式目前頁面加載進度
 */
 public void onProgressChanged(WebView view, int newProgress)
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20

2、WebChromeClient之onJsAlert、onJsConfirm、onJsPrompt

這一小節我們把這三個函數一起看,因為他們都是為了處理彈出框。

(1)、為何JS中的alert()、confirm()、prompt()無效

首先,我們來舉個例子,在網頁中加上按鈕,在點選時分别調用alert()、confirm()、prompt()來彈出不同的對話框 

web.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 id="h">歡迎光臨啟艦的blog</h1>
    <button onclick="confirm('傻叉')">confirm</button>
    <button onclick="alert('傻叉')">alert</button>
    <button onclick="prompt('傻叉')">prompt</button>
</body>
</html>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

然後是Java處理代碼:(MyActivity.java)

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

效果圖如下: 

WebView使用詳解(三)——WebChromeClient

從效果圖中,雖然啟用了JavaScript但是在網頁中的confrim()、alert()、prompt()卻是沒有效果! 

這是因為我們需要設定WebChromClient! 

在程式中,我們隻需要加上

mWebView.setWebChromeClient(new WebChromeClient());

就可以實作confrim()、alert()、prompt()的彈出效果了 

html代碼不變,我們來看一下JAVA代碼:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient());

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

相比上面,隻多了一句:mWebView.setWebChromeClient(new WebChromeClient()); 

現在再來看下效果圖: 

WebView使用詳解(三)——WebChromeClient

至于為什麼會這樣,我也不知道,也沒仔細去研究它的源碼,記住就好了。

(2)、使用onJsAlert攔截alert()函數概述

我們先來看一下在程式中如何來做的:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
                result.confirm();
                return true;
            }
        });

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

效果圖如下: 

我們在重寫onJsAlert中,在onJsAlert代碼中寫了三句話:

Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
result.confirm();
return true;
           
  • 1
  • 2
  • 3

這三句話,句句非常重要: 

第一句:

Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
           
  • 1

表示我們攔截html中alert函數之後,我們自己的操作,這裡是彈出一行toast 

第二句:

result.confirm();
           
  • 1

這句話非常重要,它表示向WebView通知操作結果,JsResult有兩個函數:JsResult.confirm()和JsResult.cancel(),JsResult.confirm()表示點選了彈出框的确定按鈕,JsResult.cancel()則表示點選了彈出框的取消按鈕。 

如果沒有使用JsResult來告訴WebView處理結果,則WebView就會認為這個彈出框還一直彈在那裡,你再點選alert按鈕,将會無效; 

第三句:

return true;
           
  • 1

表示告訴WebView我們已經攔截了alert()函數,不需要再彈出網頁中的alert彈出框了,如果我們return false,那麼WebView就會認為我們沒有攔截alert()函數,會繼續彈出alert對話框。

(3)、如果onJsAlert沒有使用JsResult确認結果

如果我們把result.confirm()去掉,來看一下,是不是真的像我們說的那樣再次點選alert按鈕會失效

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
//                result.confirm();
                return true;
            }
        });

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

我們把上面代碼中的 result.confirm()給注掉一下,看下效果: 

WebView使用詳解(三)——WebChromeClient

從效果圖中可以看到,在去掉 result.confirm()以後,隻有第一次有效果,後面再點選alert按鈕就無效了(動畫中看不出來點選效果,其實我一直在點……)。

(4)、如果onJsAlert中return false會怎樣

前面我們也講到,如果在onJsAlert中return true,則表示告訴WebView我們已經攔截了alert函數,系統不需要再彈出alert對話框了,如果return false,則表示告訴WebView我們沒有攔截alert函數,使用系統的預設處理,從WebChromeClient的源碼中可以看到onJsAlert預設是return false的。

public boolean onJsAlert(WebView view, String url, String message,JsResult result) {
    return false;
}
           
  • 1
  • 2
  • 3

下面我們就來看看如何我們在彈出toast以後return false,系統是不是真的還會彈出alert對話框

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
                Toast.makeText(MyActivity.this,"xxx",Toast.LENGTH_SHORT).show();
                result.confirm();
                return false;
            }
        });

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

效果圖如下: 

WebView使用詳解(三)——WebChromeClient

從效果圖中也明顯看出了我們的結論是正确的,當return false時,不管我們前面做了什麼,依然會彈出對話框。

好了,有關onJsAlert我們總結一下:
  • 如果需要使網頁中的confrim()、alert()、prompt()函數生效,需要設定WebChromeClient!
  • 在使用onJsAlert來攔截alert對話框時,如果不需要再彈出alert對話框,一定要return true;在return false以後,會依然調用系統的預設處理來彈出對話框的
  • 如果我們return true,則需要在處理完成以後調用JsResult.confirm()或者JsResult.cancel()來告訴WebView我們點中哪個按鈕來取消程式對話框。否則再次點選按鈕将會失敗

可能有些同學不知道confrim(),prompt()的對話框效果,下面就整體給大家示範一下html中原生的效果。 

WebView使用詳解(三)——WebChromeClient

有關confrim()和prompt()的攔截,我們就不再講了,與攔截alert()一樣!

3、WebChromeClient之onConsoleMessage

當html中調用console相關輸出的時候,就會通過onConsoleMessage進行通知

public boolean onConsoleMessage(ConsoleMessage consoleMessage)
           
  • 1

參數意義:

  • ConsoleMessage consoleMessage:儲存着目前消息的類型和消息内容
  • 傳回值:如果傳回true時,就表示攔截了console輸出,系統就不再通過console輸出出來了,如果傳回false則表示沒有攔截console輸出,調用系統預設處理。

我們來看下正常情況下,console輸出的内容及用法 

先來看html内容:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>
    <h1 id="h">歡迎光臨啟艦的blog</h1>
    <button onclick="confirm('傻叉')">confirm</button>
    <button onclick="alert('傻叉')">alert</button>
    <button onclick="prompt('傻叉')">prompt</button>
    <button onclick="log()">log</button>
</body>
<script type="text/javascript">
function log(){
  console.log("console.log");
  console.warn("warnning");
  console.error("error");
}
</script>
</html>
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

對應的java代碼如下:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());

        mWebView.loadUrl("file:///android_asset/web.html");
    }
}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

除了alert,prompt,confirm以外,其它時候都不需要強制設定WebChromeClient 

當點選log按鈕時,會調用console的函數把log輸出出來 

效果圖如下: 

WebView使用詳解(三)——WebChromeClient

在我們logcat中也可以看到如下日志: 

WebView使用詳解(三)——WebChromeClient

示例:攔截Console,彈出消息

下面我們就重寫WebChromeClient的onConsoleMessage方法,把console消息攔截掉,然後把消息利用toast彈出來:

mWebView.setWebChromeClient(new WebChromeClient(){
    @Override
    public boolean onConsoleMessage(ConsoleMessage consoleMessage) {
        Toast.makeText(MyActivity.this,consoleMessage.message(),Toast.LENGTH_SHORT).show();
        return  true;
    }

});
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

效果圖如下: 

WebView使用詳解(三)——WebChromeClient

由于我們return true,控制台将不會再看到我們的消息日志了。

4、WebChromeClient之onProgressChanged

表示目前頁面的加載速度,函數聲明如下:

public void onProgressChanged(WebView view, int newProgress)
           
  • 1
  • WebView view:目前WebView執行個體
  • int newProgress:目前的加載進度,值從0到100

比如我們加載“http://blog.csdn.net/harvic880925” 

然後把加載進度用日志打出來:

public class MyActivity extends Activity {
    private WebView mWebView;
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        mWebView = (WebView)findViewById(R.id.webview);
        mWebView.getSettings().setJavaScriptEnabled(true);
        mWebView.setWebViewClient(new WebViewClient());
        mWebView.setWebChromeClient(new WebChromeClient(){
            @Override
            public void onProgressChanged(WebView view, int newProgress) {
                Log.e("qijian","progress:"+newProgress);
                super.onProgressChanged(view, newProgress);
            }
        });

        mWebView.loadUrl("http://blog.csdn.net/harvic880925");
    }
}
           
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

日志如下: 

WebView使用詳解(三)——WebChromeClient

大家一定要注意,底層實作時,是利用handler來定時輪循目前進度的,每隔一定時間查詢一次,是以每次拿到的進度資料是不一樣的,也就是說如果頁面較簡單,可能會直接傳回100,而跳過中間的各個資料。也就是說,除了100,其它任何一個數值不是一定傳回的,是以大家如果要用到進度除了數值100可以用等号來判斷,其它一定要用大于号或小于号,如果用了等号,可能永遠也不會執行到!

5、WebChromeClient之其它函數

WebChromeClient除了上面幾個常用函數以後,還有其它一些函數,下面就簡單列舉一下,就不再一一舉例了

/*
* 通知頁面标題變化
*/
nReceivedTitle(WebView view, String title)

/*
* 通知目前頁面網站新圖示
*/
onReceivedIcon(WebView view, Bitmap icon)

/*
* 通知主程式圖示按鈕URL
*/
onReceivedTouchIconUrl(WebView view, String url, boolean precomposed)

/*
* 通知主程式目前頁面将要顯示指定方向的View,該方法用來全屏播放視訊。
*/
public interface CustomViewCallback {
       // 通知目前頁面自定義的View被關閉
       public void onCustomViewHidden();
   }
onShowCustomView(View view, CustomViewCallback callback)

/*
* 與onShowCustomView對應,通知主程式目前頁面将要關閉Custom View
*/
onHideCustomView()

/**
 * 請求主程式建立一個新的Window,如果主程式接收請求,傳回true并建立一個新的WebView來裝載Window,然後添加到View中,發送帶有建立的WebView作為參數的resultMsg的給Target。如果主程式拒絕接收請求,則方法傳回false。預設不做任何處理,傳回false
 */
onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg)

/*
* 顯示目前WebView,為目前WebView擷取焦點。
*/
onRequestFocus(WebView view)

/*
* 通知主程式關閉WebView,并從View中移除,WebCore停止任何的進行中的加載和JS功能。
*/
onCloseWindow(WebView window)

/**
 * 告訴用戶端顯示離開目前頁面的導航提示框。如果傳回true,由用戶端處理确認提示框,調用合适的JsResult方法。如果傳回false,則傳回預設值true給javascript接受離開目前頁面的導航。預設:false。JsResult設定false,目前頁面取消導航提示,否則離開目前頁面。
 */
onJsBeforeUnload(WebView view, String url, String message, JsResult result)

/**
 *通知主程式web内容嘗試使用定位API,但是沒有相關的權限。主程式需要調用調用指定的定位權限申請的回調。更多說明檢視GeolocationPermissions相關API。
 */
onGeolocationPermissionsShowPrompt(String origin,GeolocationPermissions.Callback callback)

/*
 * 通知程式有定位權限請求。如果onGeolocationPermissionsShowPrompt權限申請操作被取消,則隐藏相關的UI界面。
 */
onGeolocationPermissionsHidePrompt()

/**
*通知主程式web内容嘗試申請指定資源的權限(權限沒有授權或已拒絕),主程式必須調用PermissionRequest#grant(String[])或PermissionRequest#deny()。如果沒有覆寫該方法,預設拒絕。
*/
onPermissionRequest(PermissionRequest request)

/**
* 通知主程式相關權限被取消。任何相關UI都應該隐藏掉。
*/
onPermissionRequestCanceled(PermissionRequest request)

/**
* 通知主程式 執行的Js操作逾時。用戶端決定是否中斷JavaScript繼續執行。如果用戶端傳回true,JavaScript中斷執行。如果用戶端傳回false,則執行繼續。注意:如果繼續執行,重置JavaScript逾時計時器。如果Js下一次檢查點仍沒有結束,則再次提示。
*/
onJsTimeout()

/**
*當停止播放,Video顯示為一張圖檔。預設圖檔可以通過HTML的Video的poster屬性标簽來指定。如果poster屬性不存在,則使用預設的poster。該方法允許ChromeClient提供預設圖檔。
*/
getDefaultVideoPoster()

/**
* 當使用者重放視訊,在渲染第一幀前需要花費時間去緩沖足夠的資料。在緩沖期間,ChromeClient可以提供一個顯示的View。如:可以顯示一個加載動畫。
*/
getVideoLoadingProgressView()

/**
* 擷取通路曆史Item,用于連結顔色。
*/
getVisitedHistory(ValueCallback callback)

/**
* 通知用戶端顯示檔案選擇器。用來處理file類型的HTML标簽,響應使用者點選選擇檔案的按鈕操作。調用filePathCallback.onReceiveValue(null)并傳回true取消請求操作。
* FileChooserParams參數的枚舉清單:
MODE_OPEN 打開
MODE_OPEN_MULTIPLE 選中多個檔案打開
MODE_OPEN_FOLDER 打開檔案夾(暫不支援)
MODE_SAVE 儲存
*/
onShowFileChooser(WebView webView, ValueCallback filePathCallback,FileChooserParams fileChooserParams)

/**
* 解析檔案選擇Activity傳回的結果。需要和createIntent一起使用。
*/
parseResult(int resultCode, Intent data)

/**
* 建立Intent對象來啟動檔案選擇器。Intent支援可通路的簡單類型檔案資源。不支援進階檔案資源如live media capture媒體快照。如果需要通路這些資源或其他進階檔案類型資源可以自己建立Intent對象。
*/
createIntent()

/**
* 傳回檔案選擇模式
*/
getMode()

/**
* 傳回可通路MIME類型數組,如audio/*,如果沒有指定可通路類型,數組傳回為null
*/
getAcceptTypes()

/**
* 傳回優先的媒體快照類型值如Camera、Microphone。true:允許快照。false,禁止快照。使用getAcceptTypes方法确定合适的capture裝置。
*/
isCaptureEnabled()

/**
* 傳回檔案選擇器的标題。如果為null,使用預設名稱。
*/
getTitle()

/**
*指定預設選中的檔案名或為null
*/
getFilenameHint()