简单介绍
为了方便开发者实现在app内展示网页并与网页交互的需求,Android SDK提供了WebView组件
它有如下功能:
- 显示和渲染Web页面
- 直接使用html文件(网络上或本地assets中)作布局
- 可和JavaScript交互调用
- WebView控件功能强大,除了具有一般View的属性和设置外,还可以对url请求、页面加载、渲染、页面交互进行强大的处理。
基本使用
下面简单介绍下WebView的基本使用:
首先新建一个工程,在layout文件里放入一个WebView控件
<?xml version="1.0" encoding="utf-8"?>
<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"
>
<WebView
android:id="@+id/webview1"
android:layout_width="match_parent"
android:layout_height="match_parent">
</WebView>
</RelativeLayout>
然后在Activity初始化方法里写入如下代码:
String url = "https://www.kkj.cn";
WebView webView = (WebView) findViewById(R.id.web_view);
webView.loadUrl(url);
WebSettings webSettings = webview.getSettings();
webSettings.setJavaScriptEnabled(true);
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
不要忘了在AndroidManifest声明访问网络的权限:
<uses-permission android:name="android.permission.INTERNET"/>
然后bingo运行,我们会发现,纳尼,居然在外部的默认浏览器中打开了这个链接。
因为默认情况下,一个WebView提供的是不像浏览器的控件,没有开启JavaScript并且忽略网页错误。如果你的目的是仅仅显示一些HTML作为你的UI的一部分,那显示可能是没有问题的(显示正常),用户仅仅阅读网页而不需要与之交互,并且网页也不需要和用户交互。
如果你想要在webview内部打开,则需要自定义WebChromeClient 方法。
一般我们重写最多的就是shouldOverrideUrlLoading()、onPageStarted()、onPageFinished()问题。
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
//webview页面加载开始时就会执行此方法、一般用作重定向时的初始化工作
//该方法在WebView开始加载页面且仅在Main frame loading(即整页加载)时回调,一次Main frame的加载只会回调该方法一次。我们可以在这个方法里设定开启一个加载的动画,告诉用户程序在等待网络的响应。
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//该方法只在WebView完成一个页面加载时调用一次(同样也只在Main frame loading时调用),我们可以可以在此时关闭加载动画,进行其他操作。
}
});
这样重写之后,我们就能在webview里面打开我们想要打开的页面了。然而,我们发现,当我们在我们打开页面的页面中继续跳转时候,再按系统返回键,会发现,直接关闭了webview,这显然不是我们想要的结果。
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
//这是一个监听用的按键的方法,keyCode 监听用户的动作,如果是按了返回键,同时Webview要返回的话,WebView执行回退操作,因为mWebView.canGoBack()返回的是一个Boolean类型,所以我们把它返回为true
if(keyCode==KeyEvent.KEYCODE_BACK&&webview.canGoBack()){
webview.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
重写返回键之后就能实现,在网页中返回上一步。
详细看一下webview的各个知识点。
加载方式
1、loadUrl(String url)
用的最多的加载方式,
des:Load the given url.
as: webView.loadUrl(“http://www.kkj.cn/“); //加载网络网页
webView.loadUrl(“file:///android_asset/html/index.html”); //加载本地assert目录下网页
webView.loadUrl(“content://com.Android.htmlfileprovider/sdcard/kris.html”); // 加载SD卡html
2、loadData(String data, String mimeType, String encoding)
des:Load the given data into the WebView.
as:
String summary = “You scored 192 points.”;
webview.loadData(summary, “text/html”, null);
如果后台给的数据是包含html的字符串,则需要用这种方式进行加载,这种方式省流量,速度快,但是需要注意编码问题。
loadData()中的html data不能包含’#’,’%’,’\’,’?’四种特殊字符
3、loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String historyUrl)
des:Load the given data into the WebView, use the provided URL as the base URL for the content.
webview的设置
WebSettings webSettings = webview.getSettings();
//支持js事件
webSettings.setJavaScriptEnabled(true);
/* 设置为true表示支持使用js打开新的窗口 */
webSettings.setJavaScriptCanOpenWindowsAutomatically(true);
/* 设置为使用webview推荐的窗口 */
webSettings.setUseWideViewPort(true);
/* 设置网页自适应屏幕大小 ---这个属性应该是跟上面一个属性一起用 */
webSettings.setLoadWithOverviewMode(true);
/* 设置是否允许webview使用缩放的功能,我这里设为false,不允许 */
webSettings.setBuiltInZoomControls(false);
webSettings.setBuiltInZoomControls(true); //显示或不显示缩放按钮(wap网页不支持)。
webSettings.setSupportMultipleWindows(true);//设置WebView是否支持多窗口。
webSettings.setAppCacheEnabled(true); //启用或禁用应用缓存。
webSettings.setAppCachePath("");//设置应用缓存路径,这个路径必须是可以让app写入文件的。该方法应该只被调用一次,重复调用会被无视~
webSettings.setCacheMode(LOAD_DEFAULT);//用来设置WebView的缓存模式。当我们加载页面或从上一个页面返回的时候,会按照设置的缓存模式去检查并使用(或不使用)缓存。
篇幅限制,仅列了一些最常用的设置,webview还可以设置更多内容,存储,默认字体大小,默认字符编码等等,
具体可以参考,史上最全的WebSettings说明
对于webview也有
webView.setHorizontalScrollBarEnabled(false);//水平不显示
webView.setVerticalScrollBarEnabled(false); //垂直不显示
webView.setScrollBarStyle(WebView.SCROLLBARS_OUTSIDE_OVERLAY);// 隐藏滚动条
webView.requestFocus(); 设置是否获取焦点
webView.requestFocusFromTouch();
WebViewClient
主要帮助WebView处理各种通知、请求事件(例如,点击链接时候如何显示界面,页面开始加载,加载完毕之后
webview.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return false;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
//webview页面加载开始时就会执行此方法、一般用作重定向时的初始化工作
//该方法在WebView开始加载页面且仅在Main frame loading(即整页加载)时回调,一次Main frame的加载只会回调该方法一次。我们可以在这个方法里设定开启一个加载的动画,告诉用户程序在等待网络的响应。
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
//该方法只在WebView完成一个页面加载时调用一次(同样也只在Main frame loading时调用),我们可以可以在此时关闭加载动画,进行其他操作。
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
// 这里进行无网络或错误处理,具体可以根据errorCode的值进行判断,
}
shouldOverrideUrlLoading()方法分析
从实践中我们知道,当我们没有给WebView提供WebViewClient时,WebView如果要加载一个url会向ActivityManager寻求一个适合的处理者来加载该url(比如系统自带的浏览器),这通常是我们不想看到的。于是我们需要给WebView提供一个WebViewClient,并重写该方法返回true来告知WebView url的加载就在app中进行。这时便可以实现在app内访问网页
简单来说就是:
1、若没有设置 WebViewClient 则在点击链接之后由系统处理该 url,通常是使用浏览器打开或弹出浏览器选择对话框。
2、若设置 WebViewClient 且该方法返回 true ,则说明由应用的代码处理该 url,WebView 不处理。
3、若设置 WebViewClient 且该方法返回 false,则说明由 WebView 处理该 url,即用 WebView 加载该 url。
WebChromeClient
辅助WebView处理Javascript的对话框、网站图标、网站Title、加载进度等
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
// 获得网页的加载进度 newProgress为当前加载百分比
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
// 获取网页的title,客户端可以在这里动态修改页面的title
// 另外,当加载错误时title为“找不到该网页”
super.onReceivedTitle(view, title);
}
});
js的交互
webView.addJavascriptInterface()
通过addJavascriptInterface(Object, String)方法注入Java对象到WebView。这个方法允许你注入一个Java对象到网页的JavaScript 的上下文,以便通过页面中的JavaScript调用。
简单粗暴时候可以这样写:
myWebView.addJavascriptInterface(new JSInterface(){
@JavascriptInterface
public String methodA(){
return ((TelephonyManager) getSystemService(TELEPHONY_SERVICE)).getDeviceId();
}
@JavascriptInterface
public void methodB(String webMessage) { }
}, "Android");
方法可能会很多,所以我们要学会优雅一些;
mWebView.addJavascriptInterface(new JSInterface(), "Android"); //model是自定义的,随便起。
JSInterface对象:
public class JSInterface {
@JavascriptInterface
public void methodA() { }
@JavascriptInterface
public void methodB(String webMessage) { }
}
ps:SDK>=17(Android4.2)以上,必须添加@JavascriptInterface声明,为避免因用户访问不安全网页导致js漏洞盗窃用户信息等不安全行为。
js调java:
Java对象到WebView之后,我们要在js里面调用 java方法。
所有调用的方法都要写在class JSInterface{ }中。
funtion a{
winow.Android.methodA(); //可带参数
}
java调js:
在webview里面调用js方法:
webview.loadUrl("javascript:alert()");
前进后退刷新
webview是否可以返回到上一页面 webView.canGoBack()
webview返回到上一页面 webView.goBack();
webview是否可以前进 webView.canGoForward()
webview前进 webView.goForward();
webview.reload(); 刷新
举个回退的简单例子简单说明
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
判断Webview是否滑动到顶部或底部
if (webView.getContentHeight() * webView.getScale() == (webView.getHeight() + webView.getScrollY())) {
// 处于底端
}
if(webView.getScrollY() == 0){
//处于顶部
}
getScrollY() //方法返回的是当前可见区域的顶端距整个页面顶端的距离,也就是当前内容滚动的距离.
getHeight()或者getBottom() //方法都返回当前WebView这个容器的高度
getContentHeight()返回的是整个html的高度,但并不等同于当前整个页面的高度,因为WebView有缩放功能,所以当前整个页面的高度实际上应该是原始html的高度再乘上缩放比例
webView的状态
//当应用程序(存在webview)被切换到后台时,这个方法不仅仅针对当前的webview而是全局的全应用程序的webview
//它会暂停所有webview的layout,parsing,javascripttimer。降低CPU功耗。
@Override
protected void onPause() {
webview.onPause();
webview.pauseTimers();
super.onPause();
}
@Override
protected void onResume() {
webview.onResume();//重新激活WebView为活跃状态,响应网页
webview.resumeTimers();//恢复pauseTimers状态
super.onResume();
//当页面被失去焦点被切换到后台不可见状态,需要执行onPause
//通过onPause动作通知内核暂停所有的动作,比如DOM的解析、plugin的执行、JavaScript执行。
}
//销毁Webview
//在关闭了Activity时,如果Webview的音乐或视频,还在播放。就必须销毁Webview
//但是注意:webview调用destory时,webview仍绑定在Activity上
//这是由于自定义webview构建时传入了该Activity的context对象
//因此需要先从父容器中移除webview,然后再销毁webview:
rootLayout.removeView(webView);
webView.destroy();
@Override
public void onDestroy() {
if (mWebView != null) {
mWebView.destroy();
mWebView = null;
}
super.onDestroy();
}
webview缓存
webview的缓存有四种模式,根据自己的需要选择对应的缓存模式。
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
//缓存模式如下:
//LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
//LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
//LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
//LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
缓存运用:
if (NetStatusUtil.isConnected(getApplicationContext())) {
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。
} else {
webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载
}
//使用缓存需要开启这些webview的设置,并且设置缓存路径
webSettings.setDomStorageEnabled(true); // 开启 DOM storage API 功能
webSettings.setDatabaseEnabled(true); //开启 database storage API 功能
webSettings.setAppCacheEnabled(true);//开启 Application Caches 功能
String cacheDirPath = getFilesDir().getAbsolutePath() + APP_CACAHE_DIRNAME;
webSettings.setAppCachePath(cacheDirPath); //设置 Application Caches 缓存目录
清除缓存:
clearCache(true); //清除网页访问留下的缓存,由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序.
clearHistory () //清除当前webview访问的历史记录,只会webview访问历史记录里的所有记录除了当前访问记录.
clearFormData () //这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据。