对于现在的App来说,内嵌一些web网页是比较常见的了,如果只是简单的使用,那是很简单的,直接使用webview加载url就可以了,但是有时还是会涉及到各种不同的需求,这时就要求我们去设置一些参数以及会做不同的处理。
这里会从四个方面来说:
1、WebView的简单使用;
2、WebView使用WebSettings的各种设置;
3、WebView使用WebViewClient各种方法的作用;
4、WebView使用WebViewChrome各种方法的作用;
1、对于WebView的使用首先是来看他是如何加载各种资源的:
//方式1. 加载一个网页:
webView.loadUrl("http://www.baidu.com/");
//方式2:加载apk包中的html页面
webView.loadUrl("file:///android_asset/web/test.html");
//方式3:加载手机本地的html页面,这里需要注意在高版本中文件权限申请,如果没有权限会报ERR_ACCESS_DENIED
webView.loadUrl("file://"+Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + "test.html");
// 方式4: 加载 HTML 页面的一小段内容
webView.loadDataWithBaseURL(null,content,"text/html","utf-8",null);
目前这里只是加载了资源文件,并没有做任何的设置,显示效果肯定是不如意的,比如显示的界面可以左右滑动,这肯定不是我们想要的效果,我们想要的肯定是显示的界面正好适配手机屏幕,这就需要使用到WebViewSettings了,后面会讲到。
2、管理WebView的生命周期:
为了配合Activiyt的生命周期,WebView也有相对应的生命周期,接下来就一起来看看:
//激活WebView为活跃状态,能正常执行网页的响应
webView.onResume() ;
//当页面被失去焦点被切换到后台不可见状态,需要执行onPause
//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
webView.onPause();
//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
webView.pauseTimers()
//恢复pauseTimers状态
webView.resumeTimers();
//销毁Webview
//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview
//但是注意:webview调用destory时,webview仍绑定在Activity上
//这是由于自定义webview构建时传入了该Activity的context对象
//因此需要先从父容器中移除webview,然后再销毁webview:
rootLayout.removeView(webView);
webView.destroy();
3、清理缓存:
//清除网页访问留下的缓存
//由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
Webview.clearCache(true);
//清除当前webview访问的历史记录
//只会webview访问历史记录里的所有记录除了当前访问记录
Webview.clearHistory();
上面讲到WebView并没有说到它的设置,比如如何将加在的资源正好可以适配手机的屏幕,如何才能对WebView进行缩放,又要如何缓存等等,这一系列的问题都是可以通过WebViewSettings来进行设置,下来就一起来看看:
1、如何获取WebViewSettings:
WebView webView = findViewById(R.id.wb_content);
WebSettings webSettings = webView.getSettings();
2、适配手机屏幕:
// 设置网页自动适配
//if(Build.VERSION.SDK_INT < Build.VERSION_CODES.KITKAT){//小于4.4(不包括4.4)用这个
// webSettings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);
//}
// 这个是设置WebView是否支持ViewPort属性,
// ViewPort是在html中配置,主要为了适配各种屏幕,如果html中有配置这个属性,那么即使不设置这个属性也是会适配屏幕的
// 注意:当html中有这个属性时,WebView设置缩放属性是不起作用的
webSettings.setUseWideViewPort(true);
// 当html中没有配置ViewPort这个属性时,同时还需要设置下面这个属性才能适配屏幕
webSettings.setLoadWithOverviewMode(true);
html配置ViewPort如下:
简书ViewPort配置:
CSDN ViewPort配置:
关于ViewPort的讲解可以自行百度。
3、设置字体内容的大小:
//设置字体的大小,100是默认大小,200是字体放大了一倍
webSettings.setTextZoom(100);
4、设置缓存模式:
// LOAD_DEFAULT:系统默认的缓存模式,有缓存且缓存没有过期,那么就使用缓存,否则就从网络上获取
// LOAD_CACHE_ELSE_NETWORK:只要有缓存,不管缓存有没有过期都是使用缓存,没有缓存就从网络上获取
// LOAD_NO_CACHE:不管有没有缓存都是从网络上获取
// LOAD_CACHE_ONLY:只从缓存中获取,不会从网络上获取
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);
对于缓存还存在其他几种设置,不过对于android来说,只需要设置其功能开启就行,一般都是在h5中做缓存处理:
4.1、Dom Storage(Web Storage)缓存:
webSettings.setDomStorageEnabled(true);
4.2、Web SQL Database 缓存:
webSettings.setDatabaseEnabled(true);
//设置缓存,可不用设置,使用系统默认路径
webSettings.setDatabasePath("path");
4.3、Application Cache(AppCache)缓存:
webSettings.setAppCacheEnabled(true);
// 不设置,使用系统默认的路径
webSettings.setAppCachePath("path");
4.4、indexed Database(IndexedDb)缓存:
webSettings.setJavaScriptEnabled(true);
这里只是说到了开启缓存的一些设置,对于这些缓存的具体含义可以参考:
https://blog.csdn.net/cuo9958/article/details/50338823
5、使页面具有缩放功能,前提是html中没有配置ViewPort属性:
// 使页面具有缩放功能,在缩放的同时显示缩放按钮
webSettings.setBuiltInZoomControls(true);
// 多方功能开始时,隐藏缩放按钮(按钮看起来很丑)
webSettings.setDisplayZoomControls(false);
6、是否允许WebView加载不安全的链接:
// MIXED_CONTENT_NEVER_ALLOW:不安全的链接全部不允许加载,android 5.1默认
// MIXED_CONTENT_ALWAYS_ALLOW:不安全的链接总是允许加载,android 4.4及以下默认
// MIXED_CONTENT_COMPATIBILITY_MODE:不安全的链接询问用户书否允许加载
// 注意:android 5.1默认禁止http和https混合调用,需用下面设置开启
webSettings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
7、开启前端代码和android本地代码互调:
webSettings.setJavaScriptEnabled(true);
WebViewSettings中的设置,并没有涉及到网络加载的一些逻辑,对于网络加载,必定是耗时的,这时为了更好的交互效果,肯定是需要一个加载的显示动画的,而且,有时想对网页中的部分链接进行拦截,这时又该怎么做呢?这时就该WebViewClient上场了。
1、当已经加载一个网页了,这时点击网页上的连接数时,这时就会跳转到系统的浏览器,这肯定不是我们想见到的,我们肯定是想让新加载的网页还是在我们的应用中显示,这时就需要设置WebViewClient:
webView.setWebViewClient(new WebViewClient());
这样设置后,点击网页中的链接时,就不会将事件交由系统去处理,而是会让我们的应用来处理,也就说新的点击事件将会交由我们的WebView来处理;
2、网页开始加载和结束加载回调:
webView.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: 加载网页结束了,你可以取消加载动画了,但图片资源未必加载完成了");
}
});
3、当我点击网页上的链接时,我想自己处理,而不是交由应用的WebView去处理:
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url.equals("targetUrl")){
Log.d(TAG, "shouldOverrideUrlLoading: 这里就需要你自己做逻辑处理了,系统并不会去加载资源");
return true;
} else {
Log.d(TAG, "shouldOverrideUrlLoading: 返回false,意味着应用不做处理,交由WebView去处理");
return false;
}
}
});
这里的返回值就说明了点击链接的事件是交由谁去处理,返回true,WebView不会处理,需要我们自己处理,返回false的话WebView会去处理,这是我们就不需要做多余的处理了;
4、加载Web网页时,加载的并不只是一个请求,会有很多的请求去执行,比如一张图片就是一个请求,一份Css资源也是一个请求等,这时,如果想对这些url进行拦截,这也是可以做到的:
webView.setWebViewClient(new WebViewClient(){
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
if (url.equals("targetUrl")) {
InputStream data = null;
WebResourceResponse response = new WebResourceResponse("text/html","utf-8",null);
Log.d(TAG, "shouldInterceptRequest: 这里data返回的是null,可以根据需要返回," +
"这样就不会再去网络上加载对应url资源了,也就不会再去执行下面的onLoadResource()方法了");
return response;
} else {
Log.d(TAG, "shouldInterceptRequest: 没有拦截,会调用到下面的onLoadResource()方法");
return null;
}
}
@Override
public void onLoadResource(WebView view, String url) {
super.onLoadResource(view, url);
Log.d(TAG, "onLoadResource: 需要去加载的资源都会执行到这里");
}
});
5、对于网页的加载,有时候各种出错的问题都是会有的,我这里总结下我测试过的一些错误处理:
webView.setWebViewClient(new WebViewClient(){
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (errorCode == -1) {
Log.d(TAG, "onReceivedError: 加载本地文件时,文件没有找到");
} else if (errorCode == -2) {
Log.d(TAG, "onReceivedError: 没有连接网络或是找不到服务器");
}
}
@Override
public void onReceivedHttpError(WebView view, WebResourceRequest request, WebResourceResponse errorResponse) {
super.onReceivedHttpError(view, request, errorResponse);
if (errorResponse.getStatusCode() == 404) {
Log.d(TAG, "onReceivedHttpError: 只有在android 6.0以上的系统才发触发到这里");
}
}
});
注意:这里对于404的处理,目前只在6.0以上的系统才能接收到,所以针对6.0以下的系统是需要我们另想办法的。
WebChromClient是WebView的一个辅助类,可以获取h5页面的的标题,图片以及对话框,利用这个对话框的特性,可以方便android与前端的交互,有好些框架就是利用了这个特性,同时,还可以监听到h5页面的加载进度。
1、网页加载进度:
webView.setWebChromeClient(new WebChromeClient(){
@Override
public void onProgressChanged(WebView view, int newProgress) {
super.onProgressChanged(view, newProgress);
Log.d(TAG, "onProgressChanged: 加载进度 = "+newProgress);
}
});
2、获取网页标题:
webView.setWebChromeClient(new WebChromeClient(){
@Override
public void onReceivedTitle(WebView view, String title) {
super.onReceivedTitle(view, title);
Log.d(TAG, "onReceivedTitle: 网页的标题是 = "+title);
}
});
3、对于h5中的对话框,可以分为三种,一是Alert;二是confirm;三是prompt,当在h5页面中执行这三种对话框时,WebChromeClient也会有相应的方法执行,这是我们就可以做我们自己的逻辑了:
webView.setWebChromeClient(new WebChromeClient(){
@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
Log.d(TAG, "onJsAlert: h5页面执行Alert对话框了");
return super.onJsAlert(view, url, message, result);
}
@Override
public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
Log.d(TAG, "onJsConfirm: h5页面执行confirm对话框了");
return super.onJsConfirm(view, url, message, result);
}
@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
Log.d(TAG, "onJsPrompt: h5页面执行prompt对话框了");
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
这三个方法返回值都是boolean值,返回true意味着我们做了处理,返回false则说明我们没有处理,可根据实际需求进行处理。
对于WebView的使用,涉及到的四个类都已经说到了,再做个总结 https://www.jianshu.com/p/3c94ae673e2a:
@Override
protected void onDestroy() {
if (mWebView != null) {
mWebView.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
mWebView.clearHistory();
((ViewGroup) mWebView.getParent()).removeView(mWebView);
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}