从 Android WebView 谈开去

文章目录
  1. 1. WebView 的简介
  2. 2. Java 与 JavaScript 之间的交互
  3. 3. Demo
  4. 4. 参考文献

目标赋予我们生命的意义和目的。有了目标,我们才会把注意力集中在追求喜悦,而不是在避免痛苦上。

Android 移动设备如手机内置有高性能的 WebKit 内核浏览器,其 SDK 中也有一个 WebView 组件。因此,一部分原生的功能改为 HTML5 开发,Android 与 HTML5 互相通信,实现混合开发。这样,该部分功能能够在不升级 App 的情况下动态更新,同时,可以在 Android 或 iOS 的 App 上运行,节约成本且提高了开发效率。本期就来谈一谈 Android WebView。

WebView 的简介

WebView,即显示网页的视图。基于该视图组件,我们可以在 Activity 中滚动 Web 浏览器,或者简单地展现一些在线内容。同时,WebView 使用 WebKit 渲染引擎来显示网页,包括通过历史记录向前和向后浏览、缩小和放大、执行文本搜索等方法。

以上来自官网。

使用 WebView,首先得在 AndroidManifest.xml 中添加访问网络权限如下(使用本地 assets 目录下的 html 文件除外):

1
<uses-permission android:name="android.permission.INTERNET" />

其次,访问页面中有 JavaScript,则设置支持 JavaScript:

1
2
WebSettings webSettings = mWebView.getSetting();
webSettings.setJavaScriptEnabled(true);

最后,WebView 的常用方法如下:

loadUrl()

加载界面:

1
2
3
4
// 加载 assets 目录下的 sample.html 文件
mWebView.loadUrl("file:///android_asset/sample.html");
// 加载网络资源
mWebView.loadUrl("http://iamasoldier6.com");

setWebViewClient()

(1) 设置 WebViewClient,网络资源在当前 WebView 中响应,不会跳转到系统浏览器响应:

1
2
3
4
5
6
7
8
// 官网说明显示 shouldOverrideUrlLoading() 在 API Level 24 中过时了
mWebView.setWebViewClient(new WebViewClient() {

public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
});

现在,这样写同样可以使得网络资源在当前 WebView 中响应,不会跳转到系统浏览器响应:

1
2
3
4
mWebView.setWebViewClient(new WebViewClient() {

});
mWebView.loadUrl(url);

(2) 设置开始加载网页、加载完成和加载错误时的处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
mWebView.setWebViewClient(new WebViewClient() {    

@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
// 开始加载网页时的处理
}

@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
// 网页加载完成时的处理
}

@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
// 加载网页失败时的处理
}
});

setWebChromeClient()

WebChromeClient 主要用来辅助 WebView 处理 JavaScript 的对话框、网站图标、网站标题和网页加载进度等。比如显示页面加载进度,需要在 WebChromeClient 子类中重写父类的 onProgressChanged() 方法( progress 表示当前页面中加载的进度,为 1 至 100 的整数):

1
2
3
4
5
6
7
8
9
10
11
mWebView.setWebChromeClient(new WebChromeClient() {    

public void onProgressChanged(WebView view, int progress) {
setTitle("页面加载中,请稍候..." + progress + "%");
setProgress(progress * 100);

if (progress == 100) {
// 相关处理
}
}
});

setDownloadListener()

WebView 渲染的界面中或许含有可以下载文件的链接,点击该链接后,开始执行下载任务,并保存文件到本地中。

I. 创建 DownloadListener:

1
2
3
4
5
6
7
8
9
10
class MyDownloadListenter implements DownloadListener {

@Override
public void onDownloadStart(String url, String userAgent, String contentDisposition, String mimetype, long contentLength) {
// 下载任务主要有两种方式:自定义下载任务,或者调用系统的 Download 模块
Uri uri = Uri.parse(url);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
startActivity(intent);
}
}

II. 设置下载监听:

1
mWebView.setDownloadListener(new MyDownloadListenter());

goBack()

若不做处理,浏览网页,点击系统 Back 键或者点击设备物理返回键,Browser 会调用 finish() 直接退出;若希望返回上一浏览页,则在当前 Activity 中重写 onKeyDown() 方法:

1
2
3
4
5
6
7
8
public boolean onKeyDown(int keyCode, KeyEvent event) {

if ((keyCode == KEYCODE_BACK) && mWebView.canGoBack()) {
mWebView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}

Java 与 JavaScript 之间的交互

Java 端使用 addJavascriptInterface() 方式,实现与 JavaScript 端的交互。

Java 调 JavaScript

I. JavaScript 端的代码如下:

1
2
3
function javaCallJs(arg) {
document.getElementById("message").innerHTML = (arg);
}

II. Java 端调用 JavaScript 端的的方法:

1
mWebView.loadUrl("javascript:javaCallJs("+"'"+ name +"'"+")");

即调用了 JavaScript 端中的 javaCallJs() 方法,传入一个 name 参数。

JavaScript 调 Java

I. Java 端配置 JavaScript 接口:

1
mWebView.addJavascriptInterface(new JsInterface(), "Java");

II. Java 端实现接口类:

1
2
3
4
5
6
7
private class JsInterface {

@JavascriptInterface
public void showAlert(String str) {
Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
}
}

III. JavaScript 端调用 Java 端的方法:

1
<input type="button" value="我是按钮,点击调用Java端方法" onclick="window.Java.showAlert('出现此提示,则JS端调用Java端的showAlert()方法成功')"/>

window.Java.showToast() 中的 Java 即 addJavascriptInterface() 中指定的,JavaScript 端向 Java 端传递了 String 类型的参数,同时,Java 端以 Toast 的形式弹出该参数。

Demo

Demo 地址:JavaAndJsCommunicateDemo

效果动图如下:

I. 新建 JsSample.html 文件,放入 assets 目录下,javaCallJs() 是 Java 端调 JavaScript 端的方法,而 showToast() 是 JavaScript 端调 Java 端的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<script type="text/javascript">
function javaCallJs(arg) {
document.getElementById("message").innerHTML = ("Welcome" + arg);
}
</script>

</head>
<body>
<div id="message">Java端输入框输入内容,点击确定,此处显示该内容,则Java端调JS端的javaCallJs()方法成功</div>
<input type="button" value="我是按钮,点击调用Java端方法" onclick="window.Java.showAlert('出现此提示,则JS端调用Java端的showAlert()方法成功')"/>
</body>
</html>

II.布局文件 activity_main.xml 中,一个输入框与一个按钮,点击按钮调用 JavaScript 中的方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/ll_root"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="com.iamasoldier6.javaandjscommunicatedemo.MainActivity">


<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#FFD700"
android:orientation="horizontal"
android:padding="10dp">


<EditText
android:id="@+id/et_input"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:background="#FFFFFF"
android:hint="我是Java端输入框,调用JS端方法,传至WebView显示"
android:textSize="16sp"/>


<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginRight="4dp"
android:onClick="confirm"
android:text="确定"
android:textSize="16sp"/>

</LinearLayout>
</LinearLayout>

III. MainActivity 中,动态生成 WebView:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
public class MainActivity extends AppCompatActivity {

private WebView mWebView;
private LinearLayout llLayout;
private EditText etInput;
private String url = "file:///android_asset/JsSample.html";

@Override
protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
llLayout = (LinearLayout) findViewById(R.id.ll_root);
etInput = (EditText) findViewById(R.id.et_input);
initView();
}

private void initView() {

// 动态创建 WebView 添加到整体布局中
mWebView = new WebView(getApplication());
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);
mWebView.setLayoutParams(params);
llLayout.addView(mWebView);
// 当前 WebView 展示,不跳转到系统浏览器
mWebView.setWebViewClient(new WebViewClient() {

});

WebSettings settings = mWebView.getSettings();
settings.setJavaScriptEnabled(true);

mWebView.loadUrl(url);
mWebView.addJavascriptInterface(new JsInterface(), "Java");
}

public void confirm(View view) {
// Java 调用 JS
mWebView.loadUrl("javascript:javaCallJs(" + "'" + etInput.getText().toString() + "'" + ")");
}

// 页面销毁时,移除 WebView
@Override
protected void onDestroy() {
super.onDestroy();
llLayout.removeView(mWebView);
mWebView.stopLoading();
mWebView.removeAllViews();
mWebView.destroy();
}

private class JsInterface {
// JS 调用 Java
@JavascriptInterface
public void showAlert(String str) {
Toast.makeText(MainActivity.this, str, Toast.LENGTH_LONG).show();
}
}
}

至此,浅谈 Android WebView 到此结束,关于使用 WebView 的一些注意事项参见安卓 webview 的一些坑

本人才疏学浅,如有疏漏错误之处,望读者中有识之士不吝赐教,谢谢。

1
Email: [email protected] / WeChat: Wolverine623

您也可以关注我个人的微信公众号码农六哥第一时间获得博客的更新通知,或后台留言与我交流

参考文献

1.http://blog.csdn.net/qq_24530405/article/details/52067474