webview是Android展示网页信息的控件,本文就来简要讲解这个控件的用法。关于webview我找到了一些很不错的文章,我就直接贴上连接,自己就不重复造轮子了。本文仅写出我本人目前使用过的东西,未来将继续补充。
详细的方法和高级技巧请参考下列博文:
使用 WebView 处理javascript的常用对话框:http://blog.csdn.net/llbupt/article/details/7368844 (讲解WebView与JS对话框的交互)
webView常见问题和解决方法汇总:http://blog.csdn.net/t12x3456/article/details/13769731/
Android中Java和JS交互:http://droidyue.com/blog/2014/09/20/interaction-between-java-and-javascript-in-android/
WebView缓存策略:http://blog.csdn.net/moubenmao_jun/article/details/9076917
下列文章:powered by miechal zhao.
WebView开发详解(一):http://blog.csdn.net/typename/article/details/39030091 (讲解WebView的API)
WebView开发详解(二):http://blog.csdn.net/typename/article/details/39495409 (讲解WebViewClient & WebChromeClient)
WebView开发详解(三):http://blog.csdn.net/typename/article/details/40302351 (讲解webSettings)
系列博文地址:Android 浏览器内核研究与探秘
一、初识webview
1.1 放入自己的工程
使用控件仍旧是老样子,在xml中写入,在java中找到控件并进行处理。此外还要记得加访问网络的权限。
<uses-permission android:name="android.permission.INTERNET" />
xml
<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" tools:context="${relativePackage}.${activityClass}" > <WebView android:id="@+id/webView" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_centerHorizontal="true" android:layout_centerVertical="true" /> </RelativeLayout>
Java
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); WebView webView = (WebView) findViewById(R.id.webView); webView.loadUrl("http://www.baidu.com/"); }
这样其实就可以运行啦,但是你在运行的时候会发现程序启动后会有下面的界面
也就是说这个控件当前没有自己展示网页的能力,还需要调用浏览器来实现。
二、WebView的方法
2.1 加载特定网页
webView.loadUrl("http://www.baidu.com/");
2.2 加载本地的html文件
现将本地文件放入asset中,然后用下面的方法加载
webView.loadUrl("file:///android_asset/XX.html");
2.3 加载html字符串
String htmlString = "<h1>Title</h1><p>This is HTML text<br /><i>Formatted in italics</i><br />Anothor Line</p>"; // 载入这个html页面 webView.loadData(htmlString, "text/html", "utf-8");
2.4 结束加载
public void stopLoading()
2.5 获取焦点
if (!view.hasFocus()) { view.requestFocus(); }
2.6 添加js接口
addJavascriptInterface(object, name);
这里的object是一个类对象,name是js中调用的方法名。
-- 构建类对象:
public class JsInteration { @JavascriptInterface public void toastMessage(String message) { Toast.makeText(getApplicationContext(), message, Toast.LENGTH_LONG).show(); } @JavascriptInterface public void onSumResult(int result) { Log.i(LOGTAG, "onSumResult result=" + result); } }
提供了两个方法:toastMessage和onSumResult
-- 编写js代码,进行调用java的方法
function toastMessage(message) { window.control.toastMessage(message) } function sumToJava(number1, number2){ window.control.onSumResult(number1 + number2) }
通过window.control.xxx就可以进行调用啦。具体请参考:http://www.cnblogs.com/tianzhijiexian/p/4244688.html
三、使用WebViewClient
webview可以设置一个webviewclient对象,通过它就可以来进行网页加载的处理了。参考自:http://blog.csdn.net/typename/article/details/39495409
3.1 shouldOverrideUrlLoading
WebView webView = (WebView) findViewById(R.id.webView);
webView.loadUrl("http://www.baidu.com/");
webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } }
如上面展示的,webview首先调用loadURL方法,然后webviewClient触发了加载url的方法,然后将展示网页的工作自己承担了下来。重写此方法返回true表明点击网页里面的链接还是在当前的webview里跳转,不跳到浏览器那边。
如果你想要webview屏蔽某个网站的超链接,或者是要对某些特殊的url进行处理,那么就需要在这里进行修改啦。屏蔽连接的话,直接返回true即可,如果要进行url判断,可以如下操作
if (url.endsWith(".apk")) { download(url);//下载处理 }
3.2 onLoadResource
在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。如下的load会在网页加载元素时调用很多次。
@Override public void onLoadResource(WebView view, String url) { // TODO 自动生成的方法存根 super.onLoadResource(view, url); System.out.println("load"); }
3.3 onPageStarted & onPageFinished
网页开始加载和结束加载时调用,一般的过程就是onpageStarted->onLoadResource->onPageFinished
@Override public void onPageStarted(WebView view, String url, Bitmap favicon) { // TODO 自动生成的方法存根 super.onPageStarted(view, url, favicon); Log.i(TAG, "onPageStarted"); } @Override public void onPageFinished(WebView view, String url) { // TODO 自动生成的方法存根 super.onPageFinished(view, url); Log.i(TAG, "onPageFinished"); }
更多方法可以参考这篇博文:http://blog.csdn.net/typename/article/details/39495409
四、处理前进和后退操作
我们应该对webview进行前进和后退的处理,需要在activity的按键时间中处理。这样我们就能通过手机的返回键进行网页的返回了。
public boolean onKeyDown(int keyCoder, KeyEvent event) { /** * canGoBack()检查是否有可以后退的记录 * anGoForward()方法可以检查是否有可以前进的历史记录。 如果你不执行这种检查,一旦 goBack() 和 goForward()方法到达历史记录顶端,它们将什么也不做。 */ if (webView.canGoBack() && keyCoder == KeyEvent.KEYCODE_BACK) { //webView.goForward(); // 前进 webView.goBack(); // goBack()表示返回webView的上一页面,后退 return true; } return false; }
五、WebChromeClient
5.1 onReceivedTitle 获取标题
当document 的title变化时,会通知应用程序。这个函数调用时机取决于网页把title设置在什么位置,大多数网页一般把title设置到页面的前面,因此很多情况会比较早回调到这个函数。
/* * 设置应用程序的标题title * * @param view * @param title */ @Override public void onReceivedTitle(WebView view, String title) { MainActivity.this.setTitle(title); super.onReceivedTitle(view, title); }
5.2 onProgressChanged 获取进度
/* * 设置网页的加载的进度条 * * @param view * @param newProgress 最新的进度条数值 */ @Override public void onProgressChanged(WebView view, int newProgress) { // TODO 自动生成的方法存根 super.onProgressChanged(view, newProgress); MainActivity.this.getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress); Log.i(TAG, "progress = " + newProgress); }
5.3 onJsAlert 处理JS警告框
下面的代码用应用的对话框来接管JS对话框
/* * 处理javascript中的alert对话框 * * 注意:这里的result是JsResult */ @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { // TODO 自动生成的方法存根 // 构架一个builder来显示网页中的对话框 Builder builder = new Builder(MainActivity.this); builder.setTitle("提示对话框"); builder.setMessage(message); builder.setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 点击确定按钮之后,继续执行网页中的操作 result.confirm(); } }); builder.setCancelable(false); builder.create(); builder.show(); return true; }
5.4 shouldInterceptRequest
参考自:http://droidyue.com/blog/2014/11/23/block-web-resource-in-webview/
从API 11(Android 3.0)开始, shouldInterceptRequest被引入就是为了解决这一类的问题。
shouldInterceptRequest这个回调可以通知主程序WebView处理的资源(css,js,image等)请求,并允许主程序进行处理后返回数据。如果主程序返回的数据为null,WebView会自行请求网络加载资源,否则使用主程序提供的数据。注意这个回调发生在非UI线程中,所以进行UI系统相关的操作是不可以的。
shouldInterceptRequest有两种重载。
- public WebResourceResponse shouldInterceptRequest (WebView view, String url) 从API 11开始引入,API 21弃用
- public WebResourceResponse shouldInterceptRequest (WebView view, WebResourceRequest request) 从API 21开始引入
本次例子暂时使用第一种,即shouldInterceptRequest (WebView view, String url)。
webView.loadUrl("http://m.sogou.com");
webView.setWebViewClient(new WebViewClient() { private String TAG = getClass().getSimpleName(); @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @Override public WebResourceResponse shouldInterceptRequest(WebView view, String url) { Log.i(TAG, "shouldInterceptRequest url=" + url); WebResourceResponse response = null; if (url.contains("logo")) { try { InputStream localCopy = getAssets().open("droidyue.png"); response = new WebResourceResponse("image/png", "UTF-8", localCopy); } catch (IOException e) { e.printStackTrace(); } } return response; } });
我们来分析下shouldInterceptRequest的代码:
参数是webview和url,我们必须返回一个WebResourceResponse对象,表示本机处理的结果,如果不为null,那么webview就展示WebResourceResponse对象,如果为null,那么webview就会自行请求资源。
其中,WebResourceResponse需要设定三个属性,MIME类型,数据编码,数据(InputStream流形式)。
这里首先放一个图片到项目根目录中的assets中
然后,判断这个资源的url中是否包含logo这个字符,如果是就进行替换操作。
接着进行替换操作,从assets中得到一个输入流,通过WebResourceResponse的构造函数,将这个输入流转为WebResourceResponse对象,最后返回。
WebResourceResponse response = null; if (url.contains("logo")) { try { InputStream localCopy = getAssets().open("droidyue.png"); response = new WebResourceResponse("image/png", "UTF-8", localCopy); } catch (IOException e) { e.printStackTrace(); } } return response;
效果:
左图是原始的效果,右图是我们进行修改后的效果,替换的logo图片。
六、自动填写表单
自动填写表单的功能是用JS实现的,参考了这篇博文:http://blog.csdn.net/y85171642/article/details/12558137
对于特定网页填写表单可以用这样的逻辑
if (url.contains("telecom/mobile/jsp/otherlogin.jsp")) { //自动填信息 //提交表单 }
主要代码:
webView.loadUrl("http://usereg.tsinghua.edu.cn/login.php");
@Override public void onPageFinished(WebView view, String url) { // TODO 自动生成的方法存根 super.onPageFinished(view, url); Log.i(TAG, "onPageFinished"); view.loadUrl("javascript:document.getElementsByName('user_login_name')[0].value='"+ "testName" + "'"); view.loadUrl("javascript:document.getElementsByName('user_password')[0].value='"+ "testPsw" + "'"); }
这个代码是自动填写表单的代码所针对的网页是清华大学校园网,在加载完毕那个网页后就会自动填写表单,如果你想要模拟提交可以用一个button或者是直接在上面的代码的最后写一个js脚本,我这里用button来实现模拟点击按钮。
Button btn = (Button)findViewById(R.id.button); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { webView.loadUrl("javascript:do_login()"); } });
清华网页源码如下:
http://usereg.tsinghua.edu.cn/login.php
全部代码:
package com.kale.webviewtest; import android.app.AlertDialog; import android.app.AlertDialog.Builder; import android.content.DialogInterface; import android.graphics.Bitmap; import android.os.Bundle; import android.support.v7.app.ActionBarActivity; import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.Window; import android.webkit.JsResult; import android.webkit.WebChromeClient; import android.webkit.WebSettings; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.Button; public class MainActivity extends ActionBarActivity { WebView webView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); Button btn = (Button)findViewById(R.id.button); btn.setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { //webView.loadUrl("javascript:do_login()"); webView.loadUrl("javascript:do_login()"); } }); webView = (WebView) findViewById(R.id.webView); //webView.loadUrl("http://210.29.65.198/"); webView.loadUrl("http://usereg.tsinghua.edu.cn/login.php"); // 调用WebSettings设置的所有函数是异步制定的,因此我们设置某个状态并不能马上生效。 WebSettings webSettings = webView.getSettings(); webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 使用默认的缓存策略,cache没有过期就用cache // webSettings.setBlockNetworkImage(true); // 不加载网页图片资源 webSettings.setJavaScriptEnabled(true); // 设置支持javascript脚本 webSettings.setSupportZoom(true); // 支持缩放 webSettings.setBuiltInZoomControls(true); // 显示缩放按钮 webView.setWebViewClient(new WebViewClient() { private String TAG = getClass().getSimpleName(); @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { view.loadUrl(url); return true; } @Override public void onLoadResource(WebView view, String url) { // TODO 自动生成的方法存根 super.onLoadResource(view, url); Log.i(TAG, "onLoadResource"); } @Override public void onPageStarted(WebView view, String url, Bitmap favicon) { // TODO 自动生成的方法存根 super.onPageStarted(view, url, favicon); Log.i(TAG, "onPageStarted"); } @Override public void onPageFinished(WebView view, String url) { // TODO 自动生成的方法存根 super.onPageFinished(view, url); Log.i(TAG, "onPageFinished"); // view.loadUrl("javascript:document.getElementsByName('uname')[0].value='"+ "testName" + "'"); // view.loadUrl("javascript:document.getElementsByName('pass')[0].value='"+ "testPsw" + "'"); view.loadUrl("javascript:document.getElementsByName('user_login_name')[0].value='"+ "testName" + "'"); view.loadUrl("javascript:document.getElementsByName('user_password')[0].value='"+ "testPsw" + "'"); } }); webView.setWebChromeClient(new WebChromeClient() { private String TAG = getClass().getSimpleName(); /* * 设置应用程序的标题title * * @param view * @param title */ @Override public void onReceivedTitle(WebView view, String title) { MainActivity.this.setTitle(title); super.onReceivedTitle(view, title); } /* * 设置网页的加载的进度条 * * @param view * @param newProgress 最新的进度条数值 */ @Override public void onProgressChanged(WebView view, int newProgress) { // TODO 自动生成的方法存根 super.onProgressChanged(view, newProgress); MainActivity.this.getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress); Log.i(TAG, "progress = " + newProgress); } /* * 处理javascript中的alert对话框 * * 注意:这里的result是JsResult */ @Override public boolean onJsAlert(WebView view, String url, String message, final JsResult result) { // TODO 自动生成的方法存根 // 构架一个builder来显示网页中的对话框 Builder builder = new Builder(MainActivity.this); builder.setTitle("提示对话框"); builder.setMessage(message); builder.setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // 点击确定按钮之后,继续执行网页中的操作 result.confirm(); } }); builder.setCancelable(false); builder.create(); builder.show(); return true; } }); } public boolean onKeyDown(int keyCoder, KeyEvent event) { /** * canGoBack()检查是否有可以后退的记录 anGoForward()方法可以检查是否有可以前进的历史记录。 * 如果你不执行这种检查,一旦 goBack() 和 goForward()方法到达历史记录顶端,它们将什么也不做。 */ if (webView.canGoBack() && keyCoder == KeyEvent.KEYCODE_BACK) { // webView.goForward(); // 前进 webView.goBack(); // goBack()表示返回webView的上一页面,后退 return true; } return false; } }
六、WebSettings
6.1 初始化
webSetting和webView生命周期一致,webView无效后webSetting也无效
WebSettings webSettings = webView.getSettings();
6.2 setCacheMode 缓存模式
详细的缓存介绍请参考:http://blog.csdn.net/t12x3456/article/details/13745553
webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 使用默认的缓存策略,cache没有过期就用cache
6.3 setBlockNetworkImage 是否屏蔽网络图片
为了提高性能我们可以选择不加载网络图片
webSettings.setBlockNetworkImage(true); // 不加载网页图片资源
6.4 支持JS
webSettings.setJavaScriptEnabled(true); // 设置支持javascript脚本
6.5 让webView支持缩放
只有显示放大缩小按钮后才能支持双指缩放,所以请附带这条语句:webSettings.setBuiltInZoomControls(true); // 显示缩放按钮
webSettings.setSupportZoom(true); // 支持缩放
6.6 webView保存数据
webView.setSaveEnabled(true);//保存数据
6.7 放大或缩小网页
webview.setInitialScale(300);//放大300显示网页
6.8 是否显示缩放按钮
就是那个放大镜和缩小镜
webSettings.setBuiltInZoomControls(false); // 不显示缩放按钮
参考自:http://www.cnblogs.com/rayray/archive/2013/08/14/3258339.html