Android控件十六:WebView使用(二):WebViewClient與常用事件監聽

原文:https://blog.csdn.net/harvic880925/article/details/51523983

一、WebViewClient

1、概述
前面我們雖然實現了交互,但可能我們會有一個很簡單的需求,就是在開始加載網頁的時候顯示進度條,加載結束以後隱藏進度條,這要怎麼做?
這些簡單的需求,Android開發的老人們肯定都已經想到了,這些有關各種事件的回調都被封裝在WebViewClient類中了,在WebViewClient中有各種的回調方法,就是在某個事件發生時供我們監聽
使用方法如下:

	mWebView.setWebViewClient(new WebViewClient(){
		@Override
		public void onPageStarted(WebView view, String url, Bitmap favicon) {
			super.onPageStarted(view, url, favicon);
			Log.d(TAG,"onPageStarted");
		}
	 
		@Override
		public void onPageFinished(WebView view, String url) {
			super.onPageFinished(view, url);
			Log.d(TAG,"onPageFinished");
		}
	});

2、WebViewClient中函數概述
在WebViewClient中除了上面我們列舉出的onPageStarted、onPageFinished還有很多其它函數,分別是

		/**
		* 在開始加載網頁時會回調
		*/
		public void onPageStarted(WebView view, String url, Bitmap favicon) 
		
		/**
		* 在結束加載網頁時會回調
		*/
		public void onPageFinished(WebView view, String url)
		
		/**
		* 攔截 url 跳轉,在裏邊添加點擊鏈接跳轉或者操作
		*/
		public boolean shouldOverrideUrlLoading(WebView view, String url)
		
		/**
		* 加載錯誤的時候會回調,在其中可做錯誤處理,比如再請求加載一次,或者提示404的錯誤頁面
		*/
		public void onReceivedError(WebView view, int errorCode,String description, String failingUrl)
		
		/**
		* 當接收到https錯誤時,會回調此函數,在其中可以做錯誤處理
		*/
		public void onReceivedSslError(WebView view, SslErrorHandler handler,SslError error)
		
		/**
		* 在每一次請求資源時,都會通過這個函數來回調
		*/
		public WebResourceResponse shouldInterceptRequest(WebView view,String url) {
			return null;
		}		

3、WebViewClient之shouldOverrideUrlLoading
該函數的完整聲明如下:

public boolean shouldOverrideUrlLoading(WebView view, String url)

這個函數會在加載超鏈接時回調過來;所以通過重寫shouldOverrideUrlLoading,可以實現對網頁中超鏈接的攔截;
返回值是boolean類型,表示是否屏蔽WebView繼續加載URL的默認行爲,因爲這個函數是WebView加載URL前回調的,所以如果我們return true,則WebView接下來就不會再加載這個URL了,所有處理都需要在WebView中操作,包含加載。
如果我們return false,則系統就認爲上層沒有做處理,接下來還是會繼續加載這個URL的。WebViewClient默認就是return false的:

shouldOverrideUrlLoading實例:
由於每次超鏈接在加載前都會先走shouldOverrideUrlLoading回調,所以我們如果想攔截某個URL,將其轉換成其它URL可以在這裏做。

		     	public class MyActivity extends Activity {
				private WebView mWebView;
			 
				private ProgressDialog mProgressDialog;
				private String TAG = "qijian";
				@Override
				public void onCreate(Bundle savedInstanceState) {
					super.onCreate(savedInstanceState);
					setContentView(R.layout.main);
			 
					mWebView = (WebView)findViewById(R.id.webview);
					mProgressDialog = new ProgressDialog(this);
					mWebView.getSettings().setJavaScriptEnabled(true);
			 
					mWebView.setWebViewClient(new WebViewClient(){
					@Override
					public boolean shouldOverrideUrlLoading(WebView view, String url) {
						if (url.contains("blog.csdn.net")){
							view.loadUrl("http://www.baidu.com");
						}
						return false;
					}
				}  
					mWebView.loadUrl("http://blog.csdn.net/harvic880925");
				}
			}		

4、WebViewClient之onReceivedError
onReceivedError的完整聲明如下:

public void onReceivedError(WebView view, int errorCode,String description, String failingUrl)

加載錯誤的時候會產生這個回調,在其中可做錯誤處理,比如我們可以加載一個錯誤提示頁面
這裏有四個參數:

		WebView view:當前的WebView實例
		int errorCode:錯誤碼
		String description:錯誤描述
		String failingUrl:當前出錯的URL

實例:

		1,提供錯誤的URL
			mWebView.loadUrl("ht://blog.csdn.net/harvic880925");
		2,重寫該方法
			mWebView.setWebViewClient(new WebViewClient(){
				@Override
				public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
					super.onReceivedError(view, errorCode, description, failingUrl);
					mWebView.loadUrl("file:///android_asset/error.html");
				}
			});

5、WebViewClient之onReceivedSslError
我們知道HTTPS協議是通過SSL來通信的,所以當使用HTTPS通信的網址(以https://開頭的網站)出現錯誤時,就會通過onReceivedSslError回調通知過來,
它的函數聲明爲:

		/**
		* 當接收到https錯誤時,會回調此函數,在其中可以做錯誤處理
		*/
		public void onReceivedSslError(WebView view, SslErrorHandler handler,SslError error)
			
			WebView view:當前的WebView實例
			SslErrorHandler handler:當前處理錯誤的Handler,它只有兩個函數SslErrorHandler.proceed()和SslErrorHandler.cancel(),
			SslErrorHandler.proceed()表示忽略錯誤繼續加載,SslErrorHandler.cancel()表示取消加載。
			在onReceivedSslError的默認實現中是使用的SslErrorHandler.cancel()來取消加載,所以一旦出來SSL錯誤,HTTPS網站就會被取消加載了,如果想忽略錯誤繼續加載就只有重寫onReceivedSslError,並在其中調用SslErrorHandler.proceed()
			SslError error:當前的的錯誤對象,SslError包含了當前SSL錯誤的基本所有信息,大家自己去看下它的方法吧,這裏就不再展開了。

實例:

		public class MyActivity extends Activity {
			private WebView mWebView;
		 
			private ProgressDialog mProgressDialog;
			private String TAG = "qijian";
			@Override
			public void onCreate(Bundle savedInstanceState) {
				super.onCreate(savedInstanceState);
				setContentView(R.layout.main);
		 
				mWebView = (WebView)findViewById(R.id.webview);
				mProgressDialog = new ProgressDialog(this);
				mWebView.getSettings().setJavaScriptEnabled(true);
		 
				mWebView.setWebViewClient(new WebViewClient(){
					@Override
					public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
						super.onReceivedSslError(view, handler, error);
						Log.e(TAG,"sslError:"+error.toString());
					}
		 
				mWebView.loadUrl("https://www.12306.cn/");
			}
		}

注意:
當使用SslErrorHandler.proceed()來繼續加載時:
1,註釋掉super.onReceivedSslError(view, handler, error);,取消系統的默認行爲,默認是取消繼續加載的
2,調用handler.proceed();來忽略錯誤繼續加載頁面。
在SSL發生錯誤時,onReceivedError會被回調嗎?——不會

6、WebViewClient之shouldInterceptRequest
在每一次請求資源時,都會通過這個函數來回調,比如超鏈接、JS文件、CSS文件、圖片等,也就是說瀏覽器中每一次請求資源時,都會回調回來,無論任何資源!但是必須注意的是shouldInterceptRequest函數是在非UI線程中執行的,在其中不能直接做UI操作,如果需要做UI操作,則需要利用Handler來實現,該函數聲明如下:

public WebResourceResponse shouldInterceptRequest(WebView view,String url) {
				return null;
			}

該函數會在請求資源前調用,我們可以通過返回WebResourceResponse的處理結果來讓WebView直接使用我們的處理結果。如果我們不想處理,則直接返回null,系統會繼續加載該資源。

實例:假如網頁中需要加載本地的圖片,我們就可以通過攔截shouldInterceptRequest,並返回結果即可
HTML中img字段加載圖片的地址是:http://localhost/qijian.png,
在Android中,當發現要加載這個地址的資源時,我們將它換成本地的圖片

HTML中:

			<!DOCTYPE html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">
				<title>Title</title>
			</head>
			<body>
				<h1 id="h">歡迎光臨啓艦的blog</h1>
				<img src="http://localhost/qijian.png"/>
			</body>
			</html>

Java中:

			public class MyActivity extends Activity {
			private WebView mWebView;
		 
			private ProgressDialog mProgressDialog;
			private String TAG = "qijian";
			@Override
			public void onCreate(Bundle savedInstanceState) {
				super.onCreate(savedInstanceState);
				setContentView(R.layout.main);
		 
				mWebView = (WebView)findViewById(R.id.webview);
				mProgressDialog = new ProgressDialog(this);
				mWebView.getSettings().setJavaScriptEnabled(true);
		 
		 
				mWebView.setWebViewClient(new WebViewClient(){
					@Override
					public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
						try {
							if (url.equals("http://localhost/qijian.png")) {
								AssetFileDescriptor fileDescriptor =  getAssets().openFd("s07.jpg");
								InputStream stream = fileDescriptor.createInputStream();
								WebResourceResponse response = new WebResourceResponse("image/png", "UTF-8", stream);
								return response;
							}
						}catch (Exception e){
							Log.e(TAG,e.getMessage());
						}
						return super.shouldInterceptRequest(view, url);
					}
				});
		 
				mWebView.loadUrl("file:///android_asset/web.html");
		}        

7、WebViewClient之其餘函數
上面講了常用的大部分函數,還些一些函數,並不怎麼用,這裏由於篇幅有限就不再講了,把函數聲明和作用列出來供大家參考

	   /**
		* 在加載頁面資源時會調用,每一個資源(比如圖片)的加載都會調用一次
		*/
		public void onLoadResource(WebView view, String url) 
		
		/**
		*  (WebView發生改變時調用) 
		*  可以參考http://www.it1352.com/191180.html的用法
		*/
		public void onScaleChanged(WebView view, float oldScale, float newScale)
		
		/**
		* 重寫此方法才能夠處理在瀏覽器中的按鍵事件。
		* 是否讓主程序同步處理Key Event事件,如過濾菜單快捷鍵的Key Event事件。
		* 如果返回true,WebView不會處理Key Event,
		* 如果返回false,Key Event總是由WebView處理。默認:false
		*/
		public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
		
		/**
		* 是否重發POST請求數據,默認不重發。
		*/
		onFormResubmission(WebView view, Message dontResend, Message resend)
		
		/**
		* 更新訪問歷史
		*/
		doUpdateVisitedHistory(WebView view, String url, boolean isReload)
		
		/**
		* 通知主程序輸入事件不是由WebView調用。是否讓主程序處理WebView未處理的Input Event。
		* 除了系統按鍵,WebView總是消耗掉輸入事件或shouldOverrideKeyEvent返回true。
		* 該方法由event 分發異步調用。注意:如果事件爲MotionEvent,則事件的生命週期只存在方法調用過程中,
		* 如果WebViewClient想要使用這個Event,則需要複製Event對象。
		*/
		onUnhandledInputEvent(WebView view, InputEvent event)
		
		/**
		* 通知主程序執行了自動登錄請求。
		*/
		onReceivedLoginRequest(WebView view, String realm, String account, String args)
		
		/**
		* 通知主程序:WebView接收HTTP認證請求,主程序可以使用HttpAuthHandler爲請求設置WebView響應。默認取消請求。
		*/
		onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
		
		/**
		* 通知主程序處理SSL客戶端認證請求。如果需要提供密鑰,主程序負責顯示UI界面。
		* 有三個響應方法:proceed(), cancel() 和 ignore()。
		* 如果調用proceed()和cancel(),webview將會記住response,
		* 對相同的host和port地址不再調用onReceivedClientCertRequest方法。
		* 如果調用ignore()方法,webview則不會記住response。該方法在UI線程中執行,
		* 在回調期間,連接被掛起。默認cancel(),即無客戶端認證
		*/
		onReceivedClientCertRequest(WebView view, ClientCertRequest request)

二、其它事件處理

上面講了有關WebViewClient的用法,但其中還有一些小問題是WebViewClient無法解決的,比如返回按鍵、滾動事件監聽等

1、返回按鍵
如果用webview點鏈接看了很多頁以後,如果不做任何處理,點擊系統“Back”鍵,整個瀏覽器會調用finish()而結束自身,如果希望瀏覽的網頁回退而不是退出瀏覽器,需要在當前Activity中處理並消費掉該Back事件。

覆蓋Activity類的onKeyDown(int keyCoder,KeyEvent event)方法。

		public class MyActivity extends Activity {
			private WebView mWebView;
		 
			private ProgressDialog mProgressDialog;
			private String TAG = "qijian";
			@Override
			public void onCreate(Bundle savedInstanceState) {
				super.onCreate(savedInstanceState);
				setContentView(R.layout.main);
		 
				mWebView = (WebView)findViewById(R.id.webview);
				mProgressDialog = new ProgressDialog(this);
				mWebView.getSettings().setJavaScriptEnabled(true);
				mWebView.setWebViewClient(new WebViewClient());
		 
				mWebView.loadUrl("http://blog.csdn.net/harvic880925/");
			}
		 
			@Override
			public boolean onKeyDown(int keyCode, KeyEvent event) {
				//改寫物理返回鍵的邏輯
				if(keyCode==KeyEvent.KEYCODE_BACK) {
					if(mWebView.canGoBack()) {
						mWebView.goBack();//返回上一頁面 
						return true;
					} else {
						System.exit(0);//退出程序 
					}
				}
				return super.onKeyDown(keyCode, event);
			}
		}

2、滾動事件監聽
我們都知道監聽滾動事件一般都是設置setOnScrollChangedListener,可惜的是 WebView並沒有給我們提供這樣的方法,但是我們可以重寫WebView,覆蓋裏面的一個方法: protected void onScrollChanged(final int l, final int t, final int oldl,final int oldt){} 然後再對外提供一個接口,

		public class MyWebView extends WebView {
 
			private OnScrollChangedCallback mOnScrollChangedCallback;
		 
			public MyWebView(Context context) {
				super(context);
			}
		 
			public MyWebView(Context context, AttributeSet attrs) {
				super(context, attrs);
			}
		 
			public MyWebView(Context context, AttributeSet attrs, int defStyleAttr) {
				super(context, attrs, defStyleAttr);
			}
		 
			@Override
			protected void onScrollChanged(int l, int t, int oldl, int oldt) {
				super.onScrollChanged(l, t, oldl, oldt);
				if (mOnScrollChangedCallback != null) {
					mOnScrollChangedCallback.onScroll(l,t,oldl,oldt);
				}
			}
		 
			public OnScrollChangedCallback getOnScrollChangedCallback() {
				return mOnScrollChangedCallback;
			}
		 
			public void setOnScrollChangedCallback(
					final OnScrollChangedCallback onScrollChangedCallback) {
				mOnScrollChangedCallback = onScrollChangedCallback;
			}
		 
			public static interface OnScrollChangedCallback {
				public void onScroll(int left,int top ,int oldLeft,int oldTop);
			}
	}

3、如何強制使用外部瀏覽器打開網頁
如果不想在 webview 中顯示網頁,而是直接跳轉到瀏覽器的話,可以像下邊那樣調用。

			Uri uri = Uri.parse("http://www.example.com"); 
			Intent intent = new Intent(Intent.ACTION_VIEW, uri); 
			startActivity(intent);
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章