官方参考资料:WebView、Building Web Apps in WebView 。
一、综述
public class WebView extends AbsoluteLayout implements ViewTreeObserver.OnGlobalFocusChangeListener, ViewGroup.OnHierarchyChangeListener
在 android.webkit 包中, 继承关系如下:
android.webkit.WebView → android.widget.AbsoluteLayout → android.view.ViewGroup → android.view.View → android.view.View 。
WebView,用于在 Android 开发中,使用 WebKit 渲染引擎加载显示 Web 页面,包含浏览历史、放大和缩小、执行搜索文本等方法。
注意:为了 Activity 可接入网络权限并在 WebView 中加载网页,必须在 AndroidManifest.xml 文件中添加“网络许可权限”:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.sdis.sdfoodtransfer"
android:versionCode="1"
android:versionName="1.0.0.1">
...
<uses-permission android:name="android.permission.INTERNET" />
...
</manifest>
二、基本使用
默认情况下, WebView 控件不像浏览器那样,没有开启 JavaScript 功能并忽略网页错误。此情况只可用于显示 Html 内容页,不需要与用户交互时。如果想要一个完全的浏览器功能,使用 URL Intent 比使用 WebView 控件更好,URL Intent示例如下:
Uri uri = Uri.parse("http://www.example.com");
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
注意:使用 URL Intent 方式会调用客户端浏览器打开 Web 页面。
在使用 WebView 时,可以在布局文件中包含<WebView>或者在Activity的onCreate()中设置窗口是一个WebView。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<!-- 显示网页 -->
<WebView
android:id="@+id/urlWeb"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_marginTop="10dp" />
</RelativeLayout>
后台代码实现,如下:
//使用布局页的情况
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.example);
WebView web=(WebView)findViewById(R.id.saleWeb);
}
//直接在onCreate()中渲染
@Override
protected void onCreate(Bundle savedInstanceState) {
WebView webview = new WebView(this);
setContentView(webview);
}
加载简单页面,如下:
// Simplest usage: note that an exception will NOT be thrown
// if there is an error loading this page (see below).
webview.loadUrl("http://slashdot.org/"); // OR, you can also load from an HTML string:
String summary = "<html><body>You scored <b>192</b> points.</body></html>";
webview.loadData(summary, "text/html", null);
// ... although note that there are restrictions on what this HTML can do.
// See the JavaDocs for loadData() and loadDataWithBaseURL() for more info.
WebView 有几个自定义的地方,可以添加自定义的行为,如下:
[1] 创建和设置一个 WebChromeClient子类。这个类当影响浏览器 UI 的 something 发生时被调用,例如,进度更新 和 JavaScript 发送警报(见调试任务)。
[2] 创建和设置一个 WebViewClient 子类,当发生影响内容渲染的 things 时将被调用,例如,错误 或 表单提交。可以用来拦截URL加载(via shouldOverrideUrlLoading())。
[3] 修改 WebSettings,例如开启 JavaScript 用 setJavaScriptEnabled()。
[4] 通过 addJavascriptInterface(Object, String) 方法注入 Java 对象到 WebView 。此方法允许注入一个Java对象到网页的 JavaScript 的上下文,使它们可以被页面中的 JavaScript 访问。
如下是一个复杂些的例子,显示错误处理、设置和进度的通知:
// Let's display the progress in the activity title bar, like the
// browser app does.
getWindow().requestFeature(Window.FEATURE_PROGRESS); webview.getSettings().setJavaScriptEnabled(true); final Activity activity = this;
webview.setWebChromeClient(new WebChromeClient() {
public void onProgressChanged(WebView view, int progress) {
// Activities and WebViews measure progress with different scales.
// The progress meter will automatically disappear when we reach 100%
activity.setProgress(progress * 1000);
}
});
webview.setWebViewClient(new WebViewClient() {
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
Toast.makeText(activity, "Oh no! " + description, Toast.LENGTH_SHORT).show();
}
}); webview.loadUrl("http://developer.android.com/");
三、Zoom
启用内置的缩放,设置 WebSettings.setBuiltInZoomControls(boolean)(包含的api版本是CUPCAKE)。
注意:如果宽或高设置为 WRAP_CONTENT 时使用缩放,可能会导致不可预知的行为,应当避免。
四、Cookie and window management
出于安全的原因,应用程序有它的缓存、cookie 贮存等。它不共享浏览器应用程序数据。
默认情况下,通过 HTML 请求打开一个新窗口会被忽略。不管是通过 JavaScript 打开还是通过目标属性连接,都会这样。可以自定义的 WebChromeClient 用来提供打开多个窗口的行为,并以任何你想要的方式渲染。
当设备屏幕方向发生变化或者其他配置改变时,一个Activity 的标准行为是被销毁和重建。这会让 WebView 去重新加载当前的页面。如果不想这样,可以设置Activity 去处理 orientation 和 keyboardHidden 的变化,只留下WebView 。它会自动重定向自己来适应变化。读取 Handling Runtime Changes 获取更多关于在运行时配置改变时如何处理的信息。
五、Building web pages to support different screen densities
一个设备的屏幕密度是基于屏幕分辨率。一个低密度屏幕每英寸有较少的像素,一个高密度的屏幕每英寸有更多像素。一个屏幕的密度是很重要的,在其他的条件相同下,一个UI元素(例如一个按钮)在通过设置屏幕像素定义高度和宽度时,在低密度的屏幕显示较大,而在高密度的屏幕显示较小。为简单起见,Android 定义所有屏幕密度为三个广义的密度:高,中,低。
默认情况下,WebView 缩放一个网页是为了以中等密度屏幕上的默认外观的大小来描绘渲染。因此,它适用在高密度屏幕 1.5 倍缩放(因为它的像素比较小),在低密度屏幕 0.75 倍的比例缩放(因为它的像素较大)。从 ECLAIR 的API开始,WebView 支持DOM、CSS和meta 标签帮助网页开发者实现屏幕适配不同屏幕密度。
以下是可以用来处理不同屏幕密度的摘要功能:
[1] window.devicePixelRatio 的 DOM 属性,它的属性值用于指定当前设备的默认缩放比例。例如,window.devicePixelRatio 的值是1.0,那么设备被认为是一个中密度(mdpi)设备,并且默认缩放比例与网页不适配;如果,它的值是1.5,则设备被认为是一个高密度设备(hdpi),并且网页内容缩放 1.5倍;如果它的值是0.75,则设备被认为是低密度设备(ldpi),并且内容缩放 0.75 倍。
[2] -webkit-device-pixel-ratio CSS媒体查询。使用它来指定屏幕密度使用该样式表。相应的值是0.75,1,1.5,分别来表示低密度、中密度和高密度的样式。例如:
<link rel="stylesheet" media="screen and (-webkit-device-pixel-ratio:1.5)" href="hdpi.css" />
hdpi.css 样式只能用于设备像素比例是1.5,是一个高密度像素比例。
六、HTML5 Video support
在应用程序中为了支持内联 HTML5 视频,需要打开硬件加速。
七、Full screen support
为了支持视频或其他Html美容的全屏,需要设置一个实现 onShowCustomView(View, WebChromeClient.CustomViewCallback)和 onHideCustomView() 的 WebChromeClient。如果这两个方法有任何一个没有实现,那么网页内容不能全屏。当视频正在加载时,可以实现 getVideoLoadingProgressView() 来定义 View 来显示。
八、HTML5 Geolocation API support
For applications targeting Android N and later releases (API level > M) the geolocation api is only supported on secure origins such as https. For such applications requests to geolocation api on non-secure origins are automatically denied without invoking the corresponding onGeolocationPermissionsShowPrompt(String, GeolocationPermissions.Callback) method.
九、Layout size
It is recommended to set the WebView layout height to a fixed value or to MATCH_PARENT instead of using WRAP_CONTENT. When using MATCH_PARENT for the height none of the WebView's parents should use a WRAP_CONTENT layout height since that could result in incorrect sizing of the views.
Setting the WebView's height to WRAP_CONTENT enables the following behaviors:
The HTML body layout height is set to a fixed value. This means that elements with a height relative to the HTML body may not be sized correctly.
For applications targeting KITKAT and earlier SDKs the HTML viewport meta tag will be ignored in order to preserve backwards compatibility.
Using a layout width of WRAP_CONTENT is not supported. If such a width is used the WebView will attempt to use the width of the parent instead.
十、Metrics
当用户同意后,WebView 可以上传匿名诊断数据到Google。这些数据帮助 Google 提升 WebView。数据收集基于每一个有 WebView 实例的 App 。可以通过在 AndroidManifest.xml 文件中添加如下标签,退出数据上传:
<meta-data android:name="android.webkit.WebView.MetricsOptOut" android:value="true" />
数据只有在用户同意并没有Opt退出时才会被上传。
十一、应用实例
用途一:加载本地/Web资源
(1)加载本地资源,将待加载的本地资源(如 example.html )存放在assets文件夹内,调用WebView的loadUrl()方法加载:
webView = (WebView) findViewById(R.id.webView);
webView.loadUrl("file:///android_asset/example.html");
(2)加载网络资源,直接调用WebView的loadUrl()方法调用资源地址加载:
webView = (WebView) findViewById(R.id.webView);
webView.loadUrl("http://baidu.com");
用途二:在程序内打开网页
创建一个自己的WebViewClient,通过setWebViewClient关联,代码如下:
package com.example.testopen; import android.app.Activity;
import android.os.Bundle;
import android.webkit.WebView;
import android.webkit.WebViewClient; public class MainActivity extends Activity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
init(); } private void init(){
webView = (WebView) findViewById(R.id.webView);
webView.loadUrl("http://baidu.com"); //WebView加载web资源
//覆盖WebView默认使用第三方或系统默认浏览器打开网页的行为,使网页用WebView打开
webView.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// TODO Auto-generated method stub
//返回值是true的时候控制去WebView打开,为false调用系统浏览器或第三方浏览器
view.loadUrl(url);
return true;
}
});
}
}
用途三:如果访问的页面中有Javascript,则webview必须设置支持Javascript
//import android.webkit.WebSettings;
//启用支持javascript
WebSettings settings = webView.getSettings();
settings.setJavaScriptEnabled(true);
用途四:如果希望浏览的网页后退而不是退出浏览器,需要WebView覆盖URL加载,让它自动生成历史访问记录,就可以通过前进或后退访问已访问过的站点,需要 android.view.KeyEvent 命名空间。
//改写物理按键——返回的逻辑
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if(keyCode==KeyEvent.KEYCODE_BACK)
{
if(webView.canGoBack())
{
webView.goBack();//返回上一页面
return true;
}
else
{
System.exit();//退出程序
}
}
return super.onKeyDown(keyCode, event);
}
用途五:判断页面加载过程
webView.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
// TODO Auto-generated method stub
if (newProgress == ) {
// 网页加载完成
} else {
// 加载中
}
}
});
用途六:缓存的使用
优先使用缓存:
webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
不使用缓存:
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);