**
WebViewClient的基本使用
**
1. 打开网页时不调用系统浏览器, 而是在本WebView中显示:
mWebView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});
2.通过java代码调用javascript
WebSettings webSettings = mWebView .getSettings();
webSettings.setJavaScriptEnabled(true);
mWebView.addJavascriptInterface(new Object() {
public void clickOnAndroid() {
mHandler.post(new Runnable() {
public void run() {
webview.loadUrl("javascript:wave()");
}
});
}
}, "demo");
3.按返回键时, 不退出程序而是返回上一浏览页面:
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView .canGoBack()) {
webview.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
4.打开页面时, 自适应屏幕:
WebSettings webSettings = mWebView .getSettings();
webSettings.setUseWideViewPort(true);//设置此属性,可任意比例缩放
webSettings.setLoadWithOverviewMode(true);
5.便页面支持缩放:
WebSettings webSettings = mWebView .getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setBuiltInZoomControls(true);
webSettings.setSupportZoom(true);
6.如果webView中需要用户手动输入用户名、密码或其他,则webview必须设置支持获取手势焦点。
webview.requestFocusFromTouch();
7.WebView 加载界面主要调用三个方法:LoadUrl、LoadData、LoadDataWithBaseURL.
loadUrl(String url)
//直接加载网页、图片并显示.(本地或是网络上的网页、图片、gif)
loadData(String data, String mimeType, String encoding)
//显示文字与图片内容 (模拟器1.5、1.6)
loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
//显示文字与图片内容(支持多个模拟器版本)
8.WebSettings 的常用方法介绍
setJavaScriptEnabled(true); //支持js
setPluginsEnabled(true); //支持插件
setUseWideViewPort(false); //将图片调整到适合webview的大小
setSupportZoom(true); //支持缩放变焦
setBuiltInZoomControls(false); //设置是否支持缩放
setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); //支持内容重新布局
supportMultipleWindows(); //多窗口
setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //优先使用webview中缓存
setCacheMode(WebSettings.LOAD_NO_CACHE)//关闭webview中缓存
setAllowFileAccess(true); //设置可以访问文件
setNeedInitialFocus(true); //当webview调用requestFocus时为webview设置节点
webview webSettings.setBuiltInZoomControls(true); //设置支持缩放
setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口
setLoadWithOverviewMode(true); // 缩放至屏幕的大小
setLoadsImagesAutomatically(true); //支持自动加载图片
setBlockNetworkImage(true); //是否显示网络图像
setDefaultFontSize(int size); //设置默认的字体大小
setDefaultTextEncodingName(String encoding); //设置默认文字编码
setFixedFontFamily(String font); //设置固定使用的字体
setLightTouchEnabled(boolean enable); //设置用鼠标激活被选项
**9.WebViewClient 的方法
shouldOverrideUrlLoading(WebView view, String url)
/**在点击请求的是链接是才会调用,重写此方法返回true表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边。这个函数我们可以做很多操作,比如我们读取到某些特殊的URL,于是就可以不打开地址,取消这个操作,进行预先定义的其他操作,这对一个程序是非常必要的。
当加载的网页需要重定向的时候就会回调这个函数告知我们应用程序是否需要接管控制网页加载,如果应用程序接管,并且return true意味着主程序接管网页加载,如果返回false让webview自己处理。*/
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param url 即将要被加载的url
@return true 当前应用程序要自己处理这个url, 返回false则不处理。
Tips
(1) 当请求的方式是"POST"方式时这个回调是不会通知的。
(2) 当我们访问的地址需要我们应用程序自己处理的时候,可以在这里截获,比如我们发现跳转到的是一个market的链接,那么我们可以直接跳转到应用市场,或者其他app。
onPageStarted(WebView view, String url, Bitmap favicon)
//这个事件就是开始载入页面调用的,通常我们可以在这设定一个loading的页面,告诉用户程序在等待网络响应。
/**当内核开始加载访问的url时,会通知应用程序,对每个main frame这个函数只会被调用一次,页面包含iframe 或者framesets 不会另外调用一次onPageStarted,当网页内内嵌的frame 发生改变时也不会调用onPageStarted。
*/
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new WebViewClient()),即是这个webview。
@param url 即将要被加载的url
@param favicon 如果这个favicon已经存储在本地数据库中,则会返回这个网页的favicon,否则返回为null。
Tips:
(1) iframe 可能不少人不知道什么含义,这里我解释下,iframe 我们加载的一张,下面有很多链接,我们随便点击一个链接是即当前host的一个iframe.
(2) 有个问题可能是开发者困惑的,onPageStarted和shouldOverrideUrlLoading 在网页加载过程中这两个函数到底哪个先被调用。
当我们通过loadUrl的方式重新加载一个网址时候,这时候会先调用onPageStarted再调用shouldOverrideUrlLoading,当我们在打开的这个网址点击一个link,这时候会先调用shouldOverrideUrlLoading 再调用onPageStarted。不过shouldOverrideUrlLoading不一定每次都被调用,只有需要的时候才会被调用。
public void onPageFinished(WebView view, String url)
//在页面加载结束时调用。同样道理,我们知道一个页面载入完成,于是我们可以关闭loading 条,切换程序动作。
//当内核加载完当前页面时会通知我们的应用程序,这个函数只有在main frame情况下才会被调用,当调用这个函数之后,渲染的图片不会被更新,如果需要获得新图片的通知可以使用@link WebView.PictureListener#onNewPicture。
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param url 即将要被加载的url
public void doUpdateVisitedHistory(WebView view, String url,
boolean isReload)
//(更新历史记录)
//通知应用程序可以将当前的url存储在数据库中,意味着当前的访问url已经生效并被记录在内核当中。这个函数在网页加载过程中只会被调用一次。注意网页前进后退并不会回调这个函数。
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param url 当前正在访问的url
@ param isReload 如果是true 这个是正在被reload的url
public void onFormResubmission(WebView view, Message dontResend, Message resend)
//如果浏览器需要重新发送POST请求,可以通过这个时机来处理。默认是不重新发送数据。(应用程序重新请求网页数据)
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param dontResent 当浏览器不需要重新发送数据时,可以使用这个参数。
@param resent 当浏览器需要重新发送数据时, 可以使用这个参数。
public void onLoadResource(WebView view, String url)
// 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
//通知应用程序WebView即将加载url 制定的资源
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param url 即将加载的url 资源
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl)
// 当浏览器访问制定的网址发生错误时会通知我们应用程序,比如网络错误。
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param errorCode 错误号可以在WebViewClient.ERROR_* 里面找到对应的错误名称。
@param description 描述错误的信息
@param failingUrl 当前访问失败的url,注意并不一定是我们主url
Tips
在onReceiveError我们可以自定义网页的错误页面。
public void onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host, String realm)
//(获取返回信息授权请求)
//通知应用程序WebView接收到了一个Http auth的请求,应用程序可以使用supplied 设置webview的响应请求。默认行为是cancel 本次请求。
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param handler 用来响应WebView请求的对象
@param host 请求认证的host
@param realm 认真请求所在的域
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error)
// 重写此方法可以让webview处理https请求。
/**当网页加载资源过程中发现SSL错误会调用此方法。我们应用程序必须做出响应,是取消请求handler.cancel(),还是继续请求handler.proceed();内核的默认行为是handler.cancel();
*/
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param handler 处理用户请求的对象。
@param error SSL错误对象
Tips
内核会记住本次选择,如果下次还有相同的错误,内核会直接执行之前选择的结果。
public void onScaleChanged(WebView view, float oldScale, float newScale)
// (WebView发生改变时调用,比如调整适配屏幕)
//通知应用程序webview 要被scale。应用程序可以处理改事件,比如调整适配屏幕。
public void onReceivedLoginRequest(WebView view, String realm,
String account, String args)
//通知应用程序有个自动登录的帐号过程
参数说明:
@param view 请求登陆的webview
@param realm 账户的域名,用来查找账户。
@param account 一个可选的账户,如果是null 需要和本地的账户进行check, 如果是一个可用的账户,则提供登录。
@param args 验证制定参数的登录用户
onUnhandledKeyEvent(WebView view, KeyEvent event) //(Key事件未被加载时调用)
public boolean shouldOverrideKeyEvent(WebView view, KeyEvent event)
/**
*重写此方法才能够处理在浏览器中的按键事件。
*提供应用程序同步一个处理按键事件的机会,菜单快捷键需要被过滤掉。如果返回true,webview不处理该事件,如果返回false, webview会一直处理这个事件,因此在view 链上没有一个父类可以响应到这个事件。默认行为是return false;
*/
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param event 键盘事件名
@return 如果返回true,应用程序处理该时间,返回false 交有webview处理。
public WebResourceResponse shouldInterceptRequest(WebView view, String url)
//通知应用程序内核即将加载url制定的资源,应用程序可以返回本地的资源提供给内核,若本地处理返回数据,内核不从网络上获取数据。
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param url raw url 制定的资源
@return 返回WebResourceResponse包含数据对象,或者返回null
Tips
这个回调并不一定在UI线程执行,所以我们需要注意在这里操作View或者私有数据相关的动作。
如果我们需要改变网页的背景,或者需要实现网页页面颜色定制化的需求,可以在这个回调时机处理。
WebChromeClient 中的方法:
首先上张图对WebView进行一个基本的回顾:
public void onProgressChanged(WebView view, int newProgress)
/** 通知应用程序当前网页加载的进度*/
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
public void onReceivedTitle(WebView view, String title)
//当document 的title变化时,会通知应用程序
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param title document新的title
Tips
这个函数调用时机不确定,有可能很早,有可能很晚,取决于网页把title设置在什么位置,大多数网页一般把title设置到页面的前面,因此很多情况会比较早回调到这个函数。
public void onReceivedIcon(WebView view, Bitmap icon)
/**当前页面有个新的favicon时候,会回调这个函数。*/
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param icon 当前页面的favicon
/**通知应用程序 apple-touch-icon的 url */
public void onReceivedTouchIconUrl(WebView view, String url,
boolean precomposed)
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param url apple-touch-icon 的服务端地址
@param precomposed 如果precomposed 是true 则touch-icon是预先创建的
Tips
如果应用程序需要这个icon的话, 可以通过这个url获取得到 icon。
/**通知应用程序webview需要显示一个custom view,主要是用在视频全屏HTML5Video support。*/
public void onShowCustomView(View view, CustomViewCallback callback)
参数说明:
@param view 即将要显示的view
@param callback 当view 需要dismiss 则使用这个对象进行回调通知。
/**退出视频通知*/
public void onHideCustomView()
/**请求创建一个新的窗口,如果我们应用程序接管这个请求,必须返回true,并且创建一个新的webview来承载主窗口。
如果应用程序不处理,则需要返回false,默认行为和返回false表现一样。*/
public boolean onCreateWindow(WebView view, boolean isDialog,
boolean isUserGesture, Message resultMsg)
参数说明:
@param view 请求创建新窗口的webview
@param isUserGesture 如果是true,则说明是来自用户收拾操作行为,比如用户点击链接
@param isDialog true 请求创建的新窗口必须是个dialog,而不是全屏的窗口。
@param resultMsg 当webview创建时需要发送一个消息。WebView.WebViewTransport.setWebView(WebView)
Tips 具体例子如下:
private void createWindow(final Message msg) {
WebView.WebViewTransport transport = (WebView.WebViewTransport) msg.obj;
final Tab newTab = mWebViewController.openTab(null, Tab.this, true,
true);
transport.setWebView(newTab.getWebView());
msg.sendToTarget();
}
/**webview请求得到focus,发生这个主要是当前webview不是前台状态,是后台webview。*/
public void onRequestFocus(WebView view)
/**通知应用程序从关闭传递过来的webview并从view tree中remove。*/
public void onCloseWindow(WebView window)
public boolean onJsAlert(WebView view, String url, String message, JsResult result)
/**通知应用程序显示javascript alert对话框,如果应用程序返回true内核认为应用程序处理这个消息,返回false,内核自己处理。*/
参数说明:
@param view 接收WebViewClient的那个实例,前面看到webView.setWebViewClient(new MyAndroidWebViewClient()),即是这个webview。
@param url 当前请求弹出javascript 对话框webview 加载的url地址。
@param message 弹出的内容信息
@result 用来响应用户的处理。
Tips
如果我们应用接管处理, 则必须给出result的结果,result.cancel,result.comfirm必须调用其中之后,否则内核会hang住。
public boolean onJsConfirm(WebView view, String url, String message, JsResult result)
//通知应用程序提供confirm 对话框。
//参数说明同上onJsAlert
public boolean onJsPrompt(WebView view, String url, String message,
String defaultValue, JsPromptResult result)
//通知应用程序显示一个prompt对话框。
Tips
必须调用result.confirm 方法如果应用程序接管这个方法。
/**通知应用程序显示一个对话框,让用户选择是否离开当前页面,这个回调是javascript中的onbeforeunload事件,如果客户端返回true,内核会认为客户端提供对话框。默认行为是return false。*/
public boolean onJsBeforeUnload(WebView view, String url, String message, JsResult result)
// 参数说明和之前介绍的onJsAlert()相同。
/**通知应用程序webview内核web sql 数据库超出配额,请求是否扩大数据库磁盘配额。默认行为是不会增加数据库配额。*/
public void onExceededDatabaseQuota(String url, String databaseIdentifier,
long quota, long estimatedDatabaseSize, long totalQuota,
WebStorage.QuotaUpdater quotaUpdater)
参数说明:
@param url 触发这个数据库配额的url地址
@param databaseIdentifier 指示出现数据库超过配额的标识。
@param quota 原始数据库配额的大小,是字节单位bytes
@param estimatedDatabaseSize 到达底线的数据大小 bytes
@param totalQuota 总的数据库配额大小 bytes
@param quotaUpdater 更新数据库配额的对象,可以使用 quotaUpdater.updateQuota(newQuota);配置新的数据库配额大小。
public void onReachedMaxAppCacheSize(long requiredStorage, long quota, WebStorage.QuotaUpdater quotaUpdater)
//通知应用程序内核已经到达最大的appcache。
//appcache是HTML5针对offline的一个数据处理标准。
/**当前页面请求是否允许进行定位。*/
public void onGeolocationPermissionsShowPrompt(String origin,
GeolocationPermissions.Callback callback)
GeolocationPermissions.Callback的使用
public void invoke(String origin, boolean allow, boolean retain);
参数说明:
@param origin 权限设置的源地址
@param allow 是否允许定位
@retain 当前的选择是否让内核记住。
public void onGeolocationPermissionsHidePrompt()
/**这个回调是私有回调, 当页面需要请求打开系统的文件选择器,则会回调这个方法,比如我们需要上传图片,请求拍照,邮件的附件上传等等操作。
如果不实现这个私有API,则上面的请求都将不会执行。
*/
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture)
// Android > 4.1.1 调用这个方法
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType, String capture) {
openFileChooser(uploadMsg,"");
}
// 3.0 + 调用这个方法
public void openFileChooser(ValueCallback<Uri> uploadMsg,
String acceptType) {
openFileChooser(uploadMsg);
}
// Android < 3.0 调用这个方法
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
startActivityForResult(Intent.createChooser(
createCameraIntent(), "Image Browser"),
FILECHOOSER_RESULTCODE);
}
然后看一下具体的问题及解决方案:
1.为WebView自定义错误显示界面:
/**
* 显示自定义错误提示页面,用一个View覆盖在WebView
*/
protected void showErrorPage() {
LinearLayout webParentView = (LinearLayout)mWebView.getParent();
initErrorPage();
while (webParentView.getChildCount() > 1) {
webParentView.removeViewAt(0);
}
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,LayoutParams.FILL_PARENT);
webParentView.addView(mErrorView, 0, lp);
mIsErrorPage = true;
}
protected void hideErrorPage() {
LinearLayout webParentView = (LinearLayout)mWebView.getParent();
mIsErrorPage = false;
while (webParentView.getChildCount() > 1) {
webParentView.removeViewAt(0);
}
}
protected void initErrorPage() {
if (mErrorView == null) {
mErrorView = View.inflate(this, R.layout.online_error, null);
Button button = (Button)mErrorView.findViewById(R.id.online_error_btn_retry);
button.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
mWebView.reload();
}
});
mErrorView.setOnClickListener(null);
}
}
2.WebView cookies清理:
CookieSyncManager.createInstance(this);
CookieSyncManager.getInstance().startSync();
CookieManager.getInstance().removeSessionCookie();
3.清理cache 和历史记录:
webView.clearCache(true);
webView.clearHistory();
4.判断WebView是否已经滚动到页面底端:
getScrollY()//方法返回的是当前可见区域的顶端距整个页面顶端的距离,也就是当前内容滚动的距离.
getHeight()或者getBottom()方法都返回当前WebView 这个容器的高度
getContentHeight() 返回的是整个html 的高度,但并不等同于当前整个页面的高度,因为WebView 有缩放功能, 所以当前整个页面的高度实际上应该是原始html 的高度再乘上缩放比例. 因此,更正后的结果,准确的判断方法应该是:
if(WebView.getContentHeight*WebView.getScale() == (webview.getHeight()+WebView.getScrollY())){ //已经处于底端 }
5.URL拦截:
Android WebView是拦截不到页面内的fragment跳转的。但是url跳转的话,又会引起页面刷新,H5页面的体验又下降了。只能给WebView注入JS方法了。
6.处理WebView中的非超链接请求(如Ajax请求):
有时候需要加上请求头,但是非超链接的请求,没有办法再shouldOverrinding中拦截并用webView.loadUrl(String
url,HashMap headers)方法添加请求头
目前用了一个临时的办法解决:
首先需要在url中加特殊标记/协议,
如在onWebViewResource方法中拦截对应的请求,然后将要添加的请求头,以get形式拼接到url末尾
在shouldInterceptRequest()方法中,可以拦截到所有的网页中资源请求,比如加载JS,图片以及Ajax请求等等
@SuppressLint("NewApi")
@Override
public WebResourceResponse shouldInterceptRequest(WebView view,String url) {
// 非超链接(如Ajax)请求无法直接添加请求头,现拼接到url末尾,这里拼接一个imei作为示例
String ajaxUrl = url;
// 如标识:req=ajax
if (url.contains("req=ajax")) {
ajaxUrl += "&imei=" + imei;
}
return super.shouldInterceptRequest(view, ajaxUrl);
}
7.在页面中先显示图片:
@Override
public void onLoadResource(WebView view, String url) {
mEventListener.onWebViewEvent(CustomWebView.this, OnWebViewEventListener.EVENT_ON_LOAD_RESOURCE, url);
if (url.indexOf(".jpg") > 0) {
hideProgress(); //请求图片时即显示页面
mEventListener.onWebViewEvent(CustomWebView.this, OnWebViewEventListener.EVENT_ON_HIDE_PROGRESS, view.getUrl());
}
super.onLoadResource(view, url);
}
8.屏蔽掉长按事件 因为webview长按时将会调用系统的复制控件:
mWebView.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
return true;
}
});
9.在WebView加入 flash支持:
String temp = "<html><body bgcolor=\"" + "black"
+ "\"> <br/><embed src=\"" + url + "\" width=\"" + "100%"
+ "\" height=\"" + "90%" + "\" scale=\"" + "noscale"
+ "\" type=\"" + "application/x-shockwave-flash"
+ "\"> </embed></body></html>";
String mimeType = "text/html";
String encoding = "utf-8";
web.loadDataWithBaseURL("null", temp, mimeType, encoding, "");