Android中使用WebView與JS交互全解析
1.概述
首先,需要提出一個概念,那就是hybrid,主要意思就是native原生Android和h5混合開發。為什么要這樣做呢?大家可以想象一下針對于同一個活動,如果使用純native的開發方式,Android和iOS兩邊都要維護同一套界面甚至是邏輯,這樣開發和維護的成本會很大,而使用hybrid的開發方式的話,讓前端的同學去寫一套界面和邏輯,對于native端來說只要使用對應的容器去展示就可以了(對于Android來說這個容器當然就是WebView)。那為什么不所有的頁面都使用這種方式開發呢?因為使用h5來展示界面的話用戶體驗始終是不如native的,所以在這兩者之間我們需要一個權衡。
介紹完了何為hybrid,我們來思考下面幾個場景:
場景1:前端那邊的頁面有一個按鈕,點擊這個按鈕需要顯示一個native的組件(比如一個toast),或者點擊這個按鈕需要去在native端執行一個耗時的任務。
場景2:還是前端頁面有一個按鈕,點擊這個按鈕的邏輯是:如果登錄了,則跳轉到相應的界面,如果沒有登錄,則跳轉到登錄界面。而這個登錄界面是我們native維護的。
看完上面兩個場景,相信大家也發現了一個問題,hybrid這樣的開發方式有一個問題需要解決,那就是前端和本地的通信。
下面將會給大家介紹active原生Android和h5之間的通信方式。
2.如何使用WebView
使用WebView控件 與其他控件的使用方法相同 在layout中使用一個”WebView”標簽
WebView不包括導航欄,地址欄等完整瀏覽器功能,只用于顯示一個網頁
在WebView中加載Web頁面,使用loadUrl()
注意在manifest文件中加入訪問互聯網的權限:
1. <uses-permission android:name=”android.permission.INTERNET”/>
但是,在Android中點擊一個鏈接,默認是調用手機上已經安裝的瀏覽器程序來啟動,因此想要通過WebView代為處理這個動作 ,那么需要通過WebViewClient
當然,我們也可以寫一個類繼承WebViewClient來對WebViewClient對象進行擴展
然后只需要將setWebViewClient的內容進行修改即可
另外出于用戶習慣上的考慮 需要將WebView表現得更像一個瀏覽器,也就是需要可以回退歷史記錄,因此需要覆蓋系統的回退鍵 goBack,goForward可向前向后瀏覽歷史頁面
例子1:WebViewClient的使用
布局代碼activity_main.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"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.example.hybirddemo.MainActivity" >
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/webView" />
</RelativeLayout>
MainActivity代碼:
public class MainActivity extends Activity {
private WebView webView;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 獲取webview控件
webView = (WebView) findViewById(R.id.webView);
//設置WebViewClient
/webView.setWebViewClient(new MyWebViewClient());/
//使用webview加載頁面
webView.loadUrl("
@Override
//覆蓋系統的回退鍵
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}</code></pre>
3.JavaScript和java的相互調用
WebSetting用處非常大,通過WebSetting可以使用Android原生的JavascriptInterface來進行js和java的通信。
例子2:JavaScript和java的相互調用
首先我們寫一段html代碼:
<!DOCTYPE html>
<html>
<head>
<title>MyHtml.html</title>
<meta http-equiv="keywords" content="keyword1,keyword2,keyword3">
<meta http-equiv="description" content="this is my page">
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<!--<link rel="stylesheet" type="text/css" href="./styles.css">-->
<script type="text/javascript">
function showToast(toast) {
javascript:control.showToast(toast);
}
function log(msg) {
consolse.log(msg);
}
</script>
</head>
<body>
<input type="button" value="toast" onclick="showToast('hello world!')">
</body>
</html>
這是一個很簡單的html5頁面,里面有一個button,點擊這個button就執行js腳本中的showToast方法。

那么這個showToast方法做了什么呢?

可以看到control.showToast,這個是什么我們后面再說,下面看我們Android工程中的java代碼。
編寫布局文件activity_main.xml
布局的內容很簡單,就是嵌套一個WebView控件

編寫MainActivity.java代碼
public class MainActivity extends Activity {
private WebView webView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// 獲取webview控件
webView = (WebView) findViewById(R.id.webView);
//設置WebViewClient
/*webView.setWebViewClient(new MyWebViewClient());*/
//使用webview加載頁面
webView.loadUrl("http://www.baidu.com");
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return true;
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
// TODO Auto-generated method stub
super.onPageStarted(view, url, favicon);
}
@Override
public void onPageFinished(WebView view, String url) {
// TODO Aut (view, url);
}
});
}
@Override
//覆蓋系統的回退鍵
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && webView.canGoBack()) {
webView.goBack();
return true;
}
return super.onKeyDown(keyCode, event);
}
}</code></pre>
上面的代碼主要做了以下的步驟:
a) 獲取webview控件
b) 獲取webview的設置,將JavaScript設置為可用,打開JavaScript的通道

c) 在Android程序中建立接口 ,并編寫相關邏輯
再去看之前js腳本中的那個showToast()方法

這里的control就是我們的那個interface,調用了interface的showToast方法,很明顯這里是js調用了Android的代碼,輸出了一個Toast

可以看到這個interface我們給它取名叫control,最后通過loadUrl加載頁面。

可以看到先顯示一個toast,然后調用log()方法,log()方法里調用了js腳本的log()方法, js的log()方法做的事就是在控制臺輸出msg,這里明顯是Android調用了js的方法。

d) 給webview添加我們自己編寫的JavaScript接口
通過WebView的addJavascriptInterface方法去注入一個我們自己寫的interface。

e) 使用webview控件加載我們之前編寫的html文件

在真實手機上運行程序,在控制臺成功輸出內容:

這樣我們就完成了js和java的互調,是不是很簡單。
4.Android中處理JS的警告,對話框等
在Android中處理JS的警告,對話框等需要對WebView設置WebChromeClient對象,并復寫其中的onJsAlert,onJsConfirm,onJsPrompt方法可以處理javascript的常用對話框
例子3:在Android中處理javascript的對話框
1) 編寫html頁面布局
<%@LANGUAGE="JAVASCRIPT" CODEPAGE="936"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" ";
<html xmlns=";
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8 " />
<title>分別測試javascript的三種對話框</title>
<script language="javascript">
function ale()
{
alert("這是一個警告對話框!");
}
function firm()
{
if(confirm("更多信息請到我的博客去?"))
{
location.href="
function prom()
{
var str=prompt("演示一個帶輸入的對話框","這里輸入你的信息");
if(str)
{
alert("謝謝使用,你輸入的是:"+ str)
}
}
</script>
</head>
<body>
<p>下面我們演示3種對話框</p>
<p>警告、提醒對話框</p>
<p>
<input type="submit" name="Submit" value="提交" onclick="ale()" />
</p>
<p>帶選擇的對話框</p>
<p>
<input type="submit" name="Submit2" value="提交" onclick="firm()" />
</p>
<p>要求用戶輸入的對話框</p>
<p>
<input type="submit" name="Submit3" value="提交" onclick="prom()" />
</p>
</body>
</html></code></pre>
頁面效果:

2) Android中布局的編寫
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<LinearLayout
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:animationCache="true"
android:layout_weight="9">
<EditText
android:id="@+id/EditText01"
android:layout_width="wrap_content"
android:layout_weight="9"
android:layout_height="wrap_content"
android:text="請輸入網址"/>
<Button android:id="@+id/Button01"
android:layout_width="wrap_content"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="連接" />
</LinearLayout>
<WebView
android:id="@+id/WebView01"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1"
/>
</LinearLayout>
3) 編寫自定義對話框的布局
新建prom_dialog.xml文件,在其中自定義一個帶輸入的對話框由TextView和EditText構成
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center_horizontal"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/TextView_PROM"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="@+id/EditText_PROM"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:selectAllOnFocus="true"
android:scrollHorizontally="true"/>
</LinearLayout>
4) 獲取WebView控件,并進行相關的設置

5) 復寫onKeyDown方法,當用戶按返回鍵時,返回上一個加載的頁面

6) 給WebView設置setWebChromeClient,并復寫其中的方法
// 設置WebChromeClient
mWebView.setWebChromeClient(new WebChromeClient() {
@Override
// 處理javascript中的alert
public boolean onJsAlert(WebView view, String url, String message, final JsResult result) {
// 構建一個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) {
// TODO Auto-generated method stub
// 點擊確定按鈕之后,繼續執行網頁中的操作
result.confirm();
}
});
builder.setNegativeButton(android.R.string.cancel,
new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
builder.setCancelable(false);
builder.create();
builder.show();
return true;
}
@Override
//處理javascript中的confirm
public boolean onJsConfirm(WebView view, String url,
String message, final JsResult result) {
Builder builder = new Builder(MainActivity.this);
builder.setTitle("帶選擇的對話框");
builder.setMessage(message);
builder.setPositiveButton(android.R.string.ok,new AlertDialog.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
});
builder.setNegativeButton(android.R.string.cancel,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
});
builder.setCancelable(false);
builder.create();
builder.show();
return true;
}
@Override
// 處理javascript中的prompt
// message為網頁中對話框的提示內容
// defaultValue在沒有輸入時,默認顯示的內容
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, final JsPromptResult result) {
// 自定義一個帶輸入的對話框由TextView和EditText構成
LayoutInflater layoutInflater = LayoutInflater.from(MainActivity.this);
final View dialogView = layoutInflater.inflate(R.layout.prom_dialog, null);
// 設置TextView對應網頁中的提示信息
((TextView) dialogView.findViewById(R.id.TextView_PROM)).setText(message);
// 設置EditText對應網頁中的輸入框
((EditText) dialogView.findViewById(R.id.EditText_PROM)).setText(defaultValue);
//構建一個Builder來顯示網頁中的對話框
Builder builder = new Builder(MainActivity.this);
//設置彈出框標題
builder.setTitle("帶輸入的對話框");
//設置彈出框的布局
builder.setView(dialogView);
//設置按鍵的監聽
builder.setPositiveButton(android.R.string.ok, new AlertDialog.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// 點擊確定之后,取得輸入的值,傳給網頁處理
String value = ((EditText) dialogView.findViewById(R.id.EditText_PROM)).getText().toString();
result.confirm(value);
}
});
builder.setNegativeButton(android.R.string.cancel, new OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
result.cancel();
}
});
builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
public void onCancel(DialogInterface dialog) {
result.cancel();
}
});
builder.show();
return true;
}
@Override
//設置網頁加載的進度條
public void onProgressChanged(WebView view, int newProgress) {
MainActivity.this.getWindow().setFeatureInt(Window.FEATURE_PROGRESS, newProgress *100);
super.onProgressChanged(view, newProgress);
}
@Override
public void onReceivedTitle(WebView view, String title) {
MainActivity.this.setTitle(title);
super.onReceivedTitle(view, title);
}
});
mButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//取得編輯框中我們輸入的內容
String url = mEditText.getText().toString().trim();
//判斷輸入的內容是不是網址
if(URLUtil.isNetworkUrl(url)){
//裝載網址
mWebView.loadUrl(url);
}else{
mEditText.setText("輸入網址錯誤,請重新輸入");
}
}
});
}

圖1 dialog.html頁面

圖2 javascript的警告對話框

圖3 javascript的confirm對話框

圖4 javascript的prompt對話框
總結:在這個項目中,使用setWebChromeClient方法來為WebView設置一個WebChromeClient對象,來輔助WebView來處理Javascript的對話框等,圖4是我們自定義的對話框,圖2和圖3我們都只需要監聽按鈕的點擊事件,然后通過confirm和cancel方法將我們的操作傳遞給Javascript進行處理。當你在圖1的界面,點擊第一個按鈕時,會打開圖2的對話框,點擊第二個按鈕時,會打開圖3的對話框,同時在這里點擊確定,會跳轉到另一個頁面,當點擊第三個按鈕時,會打開圖4對話框,并且可以輸入內容。
來自:http://www.androidchina.net/6109.html