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

 

 本文由用戶 xeyb1688 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!