前言
WebView 是Android显示html内容的主要方式,当然TextView也可以加载html内容.但是WebView除了功能更加强大,最重要的是还能调用Html里的JavaScript语言,这才是我们必需学习WebView的原因.
加载内容方式
方式一:加载一个网页
mWebView.loadUrl("http://www.baidu.com");
方式二:加载应用资源文件内的网页
mWebView.loadUrl("file:///android_asset/test.html");
需要注意的一点file:/// 这里是3条斜杠,这个方式加载本地html,既可以加载网络地址的图片,也可以加载本地路径的图片
保存在assets目录的test.html (此html里的图片为网络地址)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <h1>Android WebView测试</h1> <p>下面显示图片</p> <img src="https://wow.nosdn.127.net/1/wow/wowclassic/JVIX5HPT66L41555957772316.png" alt="图片加载失败" width="100%"> </body> </html>
效果图:
保存在assets目录的test.html (此html里的图片为本地路径)
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <h1>Android WebView测试</h1> <p>下面显示图片</p> <img src="file:///storage/emulated/0/DCIM/Camera/IMG_20190524_175239.jpg" alt="图片加载失败" width="100%"> </body> </html>
需要注意的一点图片的路径前面需要添加 file:/// 这里也是3个斜杠. 效果图一上面一样,就不展示了.
方式三:加载Html代码片段
mWebView.loadData(String data,String mimeType, String encoding);
例子:
String htmlContent = "<h1>Android WebView测试</h1>
" +
"<p>下面显示图片</p>
" +
"<img src="https://wow.nosdn.127.net/1/wow/wowclassic/JVIX5HPT66L41555957772316.png" alt="图片加载失败" width="100%">";
mWebView.loadData(htmlContent, "text/html", "UTF-8");
注意! 这种加载html片段的方式不支持加载本地路径的图片,但是支持网络地址图片.
方式四 : 加载Html代码片段,并且支持加载本地图片与网络图片
如果你需要加载html代码片段,我们一般是选择这个loadDataWithBaseURL方法.因为功能会更加强大.
mWebView.loadDataWithBaseURL(String baseUrl, String data, String mimeType, String encoding, String failUrl)
解释下这里的参数
- 第一个参数 baseUrl 是文件或者图片加载的相对路径,这是一个可以细细研究的参数.因为它既可以作用于网络地址的相对网站地址,也可以作用与本地图片或者其他文件的来源相对目录(下面会围绕这个参数来一一讲解)
- 第二个参数 data是Html的代码片段
- 第三个参数 mimeType是文件类型
- 第四个参数 encoding是字符串编码
- 第五个参数 failUrl为历史Url,一般设置为null
baseUrl参数为空
下面的代码,设置baseUrl参数为空,但是图片给出了绝对网络地址,所以我们可以正常加载到网络图片
String htmlContent = "<h1>Android WebView测试</h1> " + "<p>下面显示图片</p> " + "<img src="https://wow.nosdn.127.net/1/wow/wowclassic/JVIX5HPT66L41555957772316.png" alt="图片加载失败" width="100%">"; mWebView.loadDataWithBaseURL("", htmlContent, "text/html", "utf-8",null);
下面的代码,是baseUrl参数也是为空,但是图片给出了绝对本地路径,也可以正常的加载到保存在assets文件夹里的图片
String htmlContent = "<h1>Android WebView测试</h1> " + "<p>下面显示图片</p> " + "<img src="file:///android_asset/testImage.png" alt="图片加载失败" width="100%">"; mWebView.loadDataWithBaseURL("about:blank", htmlContent, "text/html", "utf-8",null); //也可以将空白参数写成 about:blank 其实是一个意思...
baseUrl参数设置为相对地址或者路径
下面的代码,设置baseUrl参数为相对网络地址
String htmlContent = "<h1>Android WebView测试</h1> " + "<p>下面显示图片</p> " + "<img src="JVIX5HPT66L41555957772316.png" alt="图片加载失败" width="100%">"; mWebView.loadDataWithBaseURL("https://wow.nosdn.127.net/1/wow/wowclassic/", htmlContent, "text/html", "utf-8", null);
注意! 这里有一个小坑 baseUrl的参数 https://wow.nosdn.127.net/1/wow/wowclassic/ 这个路径后面一定是/ 斜杠结尾, 而html内容 src="JVIX5HPT66L41555957772316.png" 前面不能用 / 斜杠开头 , 否则就会加载失败
下面的代码,设置baseUrl参数为相对本地路径
String htmlContent = "<h1>Android WebView测试</h1> " + "<p>下面显示图片</p> " + "<img src="IMG_20190524_175239.jpg" alt="图片加载失败" width="100%">"; mWebView.loadDataWithBaseURL("file:///storage/emulated/0/DCIM/Camera/", htmlContent,"text/html", "utf-8", null);
设置是否支持JavaScript
mWebView.getSettings().setJavaScriptEnabled(false);
设置缩放
mWebView.getSettings().setSupportZoom(true);//设置是否支持缩放 mWebView.getSettings().setBuiltInZoomControls(true);//设置缩放工具
注意,配置setSupportZoom后还需要setBuiltInZoomControls 组合才能体验到缩放效果.
设置初始缩放比例
public void setInitialScale(int scaleInPercent);
设置按比例缩放文字大小
mWebView.getSettings().setDefaultFontSize(60);//设值范围 1-72
设置滚动条风格
mWebView.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET);
获取当前页面的URL
public String getUrl();
获取当前页面的原始URL
重定向后可能当前url不同, 就是http headers的Referer参数,loadUrl时为null
public String getOriginalUrl();
获取当前页面的标题
public String getTitle();
获取当前页面的favicon 网址图标
public Bitmap getFavicon();
获取当前页面的加载进度
public int getProgress();
通知WebView内核网络状态
用于设置JS属性`window.navigator.isOnline`和产生HTML5事件`online/offline`
public void setNetworkAvailable(boolean networkUp)
JavaScript
// 注入Java对象,提供给JavaScript操作 public void addJavascriptInterface(Object object, String name); // 移除已注入的Java对象,下次加载或刷新页面时生效 public void removeJavascriptInterface(String name); // 对传入的JS表达式求值,通过resultCallback返回结果 // 此函数添加于API19,必须在UI线程中调用,回调也将在UI线程 public void evaluateJavascript(String script, ValueCallback<String> resultCallback)
导航(前进后退)
// 复制一份BackForwardList public WebBackForwardList copyBackForwardList(); // 是否可后退 public boolean canGoBack(); // 是否可前进 public boolean canGoForward(); // 是否可前进/后退steps页,大于0表示前进小于0表示后退 public boolean canGoBackOrForward(int steps); // 后退一页 public void goBack(); // 前进一页 public void goForward(); // 前进/后退steps页,大于0表示前进小于0表示后退 //以当前的index为起始点前进或者后退到历史记录中指定的steps //如果steps为负数则为后退,正数则为前进 public void goBackOrForward(int steps); // 清除当前webview访问的历史记录 public void clearHistory();
Back键控制网页后退
问题:在不做任何处理前提下 ,浏览网页时点击系统的“Back”键,整个 Browser 会调用 finish()而结束自身
目标:点击返回后,是网页回退而不是推出浏览器
解决方案:在当前Activity中处理并消费掉该 Back 事件
public boolean onKeyDown(int keyCode, KeyEvent event) { if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) { mWebView.goBack(); return true; } return super.onKeyDown(keyCode, event); }
网页查找功能
// 设置网页查找结果回调 public void setFindListener(FindListener listener); // 异步执行查找网页内包含的字符串并设置高亮,查找结果会回调. public void findAllAsync (String find); // 查找下一个匹配的字符串 public void findNext (boolean forward); // 清除网页查找的高亮匹配字符串 public void clearMatches();
截屏/翻页/缩放
// 保存网页(.html)到指定文件 public void saveWebArchive(String filename); // 保存网页(.html)到文件 public void saveWebArchive(String basename, boolean autoname, ValueCallback<String> callback); // 上翻一页,即向上滚动WebView高度的一半 public void pageUp(boolean top); // 下翻一页,即向下滚动WebView高度的一半 public void pageDown(boolean bottom); // 缩放 public void zoomBy(float factor); // 放大 public boolean zoomIn(); // 缩放 public boolean zoomOut();
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();
清除缓存数据
//清除网页访问留下的缓存 //由于内核缓存是全局的因此这个方法不仅仅针对webview而是针对整个应用程序. Webview.clearCache(true); //清除当前webview访问的历史记录 //只会webview访问历史记录里的所有记录除了当前访问记录 Webview.clearHistory(); //这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据 Webview.clearFormData();
其它
// 清除SSL偏好 public void clearSslPreferences(); // 查询文档中是否有图片,查询结果将被发送到msg.getTarget() // 如果包含图片,msg.arg1 为1,否则为0 public void documentHasImages(Message msg); // 请求最近轻叩(tapped)的 锚点/图像 元素的URL,查询结果将被发送到msg.getTarget() // msg.getData()中的url是锚点的href属性,title是锚点的文本,src是图像的src public void requestFocusNodeHref(Message msg); // 请求最近触摸(touched)的 图像元素的URL,查询结果将被发送到msg.getTarget() // msg.getData()中的url是图像链接 public void requestImageRef(Message msg) // 清除证书请求偏好,添加于API21 // 在WebView收到`android.security.STORAGE_CHANGED` Intent时会自动清除 public static void clearClientCertPreferences(Runnable onCleared) // 开启网页内容(js,css,html...)调试模式,添加于API19 public static void setWebContentsDebuggingEnabled(boolean enabled) ####WebSettings类 生成一个WebView组件(有两种方式) //方式1:直接在在Activity中生成 WebView webView = new WebView(this) //方法2:在Activity的layout文件里添加webview控件: WebView webview = (WebView) findViewById(R.id.webView1); ####配置-利用WebSettings子类 >//声明WebSettings子类 WebSettings webSettings = webView.getSettings(); //如果访问的页面中要与Javascript交互,则webview必须设置支持Javascript webSettings.setJavaScriptEnabled(true); //支持插件 webSettings.setPluginsEnabled(true); //设置自适应屏幕,两者合用 webSettings.setUseWideViewPort(true); //将图片调整到适合webview的大小 webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小 //缩放操作 webSettings.setSupportZoom(true); //支持缩放,默认为true。是下面那个的前提。 webSettings.setBuiltInZoomControls(true); //设置内置的缩放控件。若为false,则该WebView不可缩放 webSettings.setDisplayZoomControls(false); //隐藏原生的缩放控件 //其他细节操作 webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); //关闭webview中缓存 webSettings.setAllowFileAccess(true); //设置可以访问文件 webSettings.setJavaScriptCanOpenWindowsAutomatically(true); //支持通过JS打开新窗口 webSettings.setLoadsImagesAutomatically(true); //支持自动加载图片 webSettings.setDefaultTextEncodingName("utf-8");//设置编码格式 // 存储(storage) // 启用HTML5 DOM storage API,默认值 false settings.setDomStorageEnabled(true); // 启用Web SQL Database API,这个设置会影响同一进程内的所有WebView,默认值 false // 此API已不推荐使用,参考:https://www.w3.org/TR/webdatabase/ settings.setDatabaseEnabled(true); // 启用Application Caches API,必需设置有效的缓存路径才能生效,默认值 false // 此API已废弃,参考:https://developer.mozilla.org/zh-CN/docs/Web/HTML/Using_the_application_cache settings.setAppCacheEnabled(true); settings.setAppCachePath(context.getCacheDir().getAbsolutePath()); // 定位(location) settings.setGeolocationEnabled(true); / 是否保存表单数据 settings.setSaveFormData(true); // 是否当webview调用requestFocus时为页面的某个元素设置焦点,默认值 true settings.setNeedInitialFocus(true); // 是否支持viewport属性,默认值 false // 页面通过`<meta name="viewport" ... />`自适应手机屏幕 settings.setUseWideViewPort(true); // 是否使用overview mode加载页面,默认值 false // 当页面宽度大于WebView宽度时,缩小使页面宽度等于WebView宽度 settings.setLoadWithOverviewMode(true); // 布局算法 settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL); // 是否支持Javascript,默认值false settings.setJavaScriptEnabled(true); // 是否支持多窗口,默认值false settings.setSupportMultipleWindows(false); // 是否可用Javascript(window.open)打开窗口,默认值 false settings.setJavaScriptCanOpenWindowsAutomatically(false); // 资源访问 settings.setAllowContentAccess(true); // 是否可访问Content Provider的资源,默认值 true settings.setAllowFileAccess(true); // 是否可访问本地文件,默认值 true // 是否允许通过file url加载的Javascript读取本地文件,默认值 false settings.setAllowFileAccessFromFileURLs(false); // 是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false settings.setAllowUniversalAccessFromFileURLs(false); // 资源加载 settings.setLoadsImagesAutomatically(true); // 是否自动加载图片 settings.setBlockNetworkImage(false); // 禁止加载网络图片 settings.setBlockNetworkLoads(false); // 禁止加载所有网络资源 // 缩放(zoom) settings.setSupportZoom(true); // 是否支持缩放 settings.setBuiltInZoomControls(false); // 是否使用内置缩放机制 settings.setDisplayZoomControls(true); // 是否显示内置缩放控件 // 默认文本编码,默认值 "UTF-8" settings.setDefaultTextEncodingName("UTF-8"); settings.setDefaultFontSize(16); // 默认文字尺寸,默认值16,取值范围1-72 settings.setDefaultFixedFontSize(16); // 默认等宽字体尺寸,默认值16 settings.setMinimumFontSize(8); // 最小文字尺寸,默认值 8 settings.setMinimumLogicalFontSize(8); // 最小文字逻辑尺寸,默认值 8 settings.setTextZoom(100); // 文字缩放百分比,默认值 100 // 字体 settings.setStandardFontFamily("sans-serif"); // 标准字体,默认值 "sans-serif" settings.setSerifFontFamily("serif"); // 衬线字体,默认值 "serif" settings.setSansSerifFontFamily("sans-serif"); // 无衬线字体,默认值 "sans-serif" settings.setFixedFontFamily("monospace"); // 等宽字体,默认值 "monospace" settings.setCursiveFontFamily("cursive"); // 手写体(草书),默认值 "cursive" settings.setFantasyFontFamily("fantasy"); // 幻想体,默认值 "fantasy" if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { // 用户是否需要通过手势播放媒体(不会自动播放),默认值 true settings.setMediaPlaybackRequiresUserGesture(true); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { // 5.0以上允许加载http和https混合的页面(5.0以下默认允许,5.0+默认禁止) settings.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW); } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { // 是否在离开屏幕时光栅化(会增加内存消耗),默认值 false settings.setOffscreenPreRaster(false); } if (isNetworkConnected(context)) { // 根据cache-control决定是否从网络上取数据 settings.setCacheMode(WebSettings.LOAD_DEFAULT); } else { // 没网,离线加载,优先加载缓存(即使已经过期) settings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK); } // deprecated settings.setRenderPriority(WebSettings.RenderPriority.HIGH); settings.setDatabasePath(context.getDir("database", Context.MODE_PRIVATE).getPath()); settings.setGeolocationDatabasePath(context.getFilesDir().getPath());
设置WebView缓存
当加载 html 页面时,WebView会在/data/data/包名目录下生成 database 与 cache 两个文件夹
请求的 URL记录保存在 WebViewCache.db,而 URL的内容是保存在 WebViewCache 文件夹下
是否启用缓存:
优先使用缓存:
WebView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
缓存模式如下:
// LOAD_CACHE_ONLY: 不使用网络,只读取本地缓存数据
// LOAD_DEFAULT: (默认)根据cache-control决定是否从网络上取数据。
// LOAD_NO_CACHE: 不使用缓存,只从网络获取数据.
// LOAD_CACHE_ELSE_NETWORK,只要本地有,无论是否过期,或者no-cache,都使用缓存中的数据。
//不使用缓存:
WebView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
结合使用(离线加载)
if (NetStatusUtil.isConnected(getApplicationContext())) { webSettings.setCacheMode(WebSettings.LOAD_DEFAULT);//根据cache-control决定是否从网络上取数据。 } else { webSettings.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);//没网,则从本地获取,即离线加载 } 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 缓存目录 //注意: 每个 Application 只调用一次 WebSettings.setAppCachePath(),WebSettings.setAppCacheMaxSize()
WebViewClient类
作用:处理各种通知 & 请求事件
常见方法1:shouldOverrideUrlLoading()
作用:打开网页时不调用系统浏览器, 而是在本WebView中显示;在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。
复写shouldOverrideUrlLoading()方法,使得打开网页时不调用系统浏览器, 而是在本WebView中显示 webView.setWebViewClient(new WebViewClient(){ @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } });
常见方法2:onPageStarted()
作用:开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
webView.setWebViewClient(new WebViewClient(){ @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { //设定加载开始的操作 } });
常见方法3:onPageFinished()
作用:在页面加载结束时调用。我们可以关闭loading 条,切换程序动作。
webView.setWebViewClient(new WebViewClient(){ @Override public void onPageFinished(WebView view, String url) { //设定加载结束的操作 } });
常见方法4:onLoadResource()
作用:在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
webView.setWebViewClient(new WebViewClient(){ @Override public boolean onLoadResource(WebView view, String url) { //设定加载资源的操作 }});
常见方法5:onReceivedError()
作用:加载页面的服务器出现错误时(如404)调用。
App里面使用webview控件的时候遇到了诸如404这类的错误的时候,若也显示浏览器里面的那种错误提示页面就显得很丑陋了,那么这个时候我们的app就需要加载一个本地的错误提示页面,即webview如何加载一个本地的页面
//步骤1:写一个html文件(error_handle.html),用于出错时展示给用户看的提示页面 //步骤2:将该html文件放置到代码根目录的assets文件夹下 //步骤3:复写WebViewClient的onRecievedError方法 //该方法传回了错误码,根据错误类型可以进行不同的错误分类处理 webView.setWebViewClient(new WebViewClient(){ @Override public void onReceivedError(WebView view, int errorCode, String description, String failingUrl){ switch(errorCode) { case HttpStatus.SC_NOT_FOUND: view.loadUrl("file:///android_assets/error_handle.html"); break; } } });
常见方法6:onReceivedSslError()
作用:处理https请求
webView默认是不处理https请求的,页面显示空白,需要进行如下设置:
webView.setWebViewClient(new WebViewClient() { @Override public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) { handler.proceed(); //表示等待证书响应 // handler.cancel(); //表示挂起连接,为默认方式 // handler.handleMessage(null); //可做其他处理 } });
WebChromeClient类
作用:辅助 WebView 处理 Javascript 的对话框,网站图标,网站标题等等。
常见使用:
常见方法1: onProgressChanged()
作用:获得网页的加载进度并显示
webview.setWebChromeClient(new WebChromeClient(){ @Override public void onProgressChanged(WebView view, int newProgress) { if (newProgress < 100) { String progress = newProgress + "%"; progress.setText(progress); } else { } });
常见方法2: onReceivedTitle()
作用:获取Web页中的标题
每个网页的页面都有一个标题,比如www.baidu.com这个页面的标题即“百度一下,你就知道”,那么如何知道当前webview正在加载的页面的title并进行设置呢?
webview.setWebChromeClient(new WebChromeClient(){ @Override public void onReceivedTitle(WebView view, String title) { titleview.setText(title); }
回调顺序
shouldOverrideUrlLoading
onProgressChanged[10]
shouldInterceptRequest
onProgressChanged[...]
onPageStarted
onProgressChanged[...]
onLoadResource
onProgressChanged[...]
onReceivedTitle/onPageCommitVisible
onProgressChanged[100]
onPageFinished
onReceivedIcon
资源加载回调:
shouldInterceptRequest() -> onLoadResource() 发生重定向时回调: onPageStarted() -> shouldOverrideUrlLoading() 直接loadUrl的回调: // 无重定向 onPageStarted() -> onPageFinished() // 有重定向,shouldOverrideUrlLoading 返回 true 时 onPageFinished 仍会执行 onPageStarted() -> redirection -> ... -> onPageFinished() 用户点击链接的回调: // shouldOverrideUrlLoading 返回 true 时不执行onPageStarted/onPageFinished shouldOverrideUrlLoading() -> ... // 无重定向 shouldOverrideUrlLoading() -> onPageStarted() -> onPageFinished() // 有重定向 shouldOverrideUrlLoading() -> onPageStarted() -> redirection -> ... -> onPageFinished() 后退/前进/刷新时回调: >onPageStarted() -> onPageFinished()
内存泄漏
直接 new WebView 并传入 application context 代替在 XML 里面声明以防止 activity 引用被滥用,能解决90+%的 WebView 内存泄漏。
vWeb = new WebView(getContext().getApplicationContext()); container.addView(vWeb);
销毁 WebView
if (vWeb != null) { vWeb.setWebViewClient(null); vWeb.setWebChromeClient(null); vWeb.loadDataWithBaseURL(null, "", "text/html", "utf-8", null); vWeb.clearHistory(); ((ViewGroup) vWeb.getParent()).removeView(vWeb); vWeb.destroy(); vWeb = null; }
在Android代码里调用JavaScript方法
方式一
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> </head> <body> <p id="text">Android调用JavaScript测试</p> </body> <script> function demo(){ alert("Android调用了JS的callJS方法"); } </script> </html>
Android代码部分:
mWebView.evaluateJavascript("javascript:demo()", new ValueCallback<String>() { @Override public void onReceiveValue(String value) { Log.e(TAG, "onReceiveValue: value="+value); } });
end