最新消息:欢迎访问Android开发中文站!商务联系微信:loading_in

Android中使用WebView与JS交互全解析

开发进阶 AndroidChina 4358浏览 0评论

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()

Android,WebView,JS

注意在manifest文件中加入访问互联网的权限:

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

但是,在Android中点击一个链接,默认是调用手机上已经安装的浏览器程序来启动,因此想要通过WebView代为处理这个动作 ,那么需要通过WebViewClient

Android,WebView,JS

当然,我们也可以写一个类继承WebViewClient来对WebViewClient对象进行扩展

Android,WebView,JS

然后只需要将setWebViewClient的内容进行修改即可

Android,WebView,JS

另外出于用户习惯上的考虑 需要将WebView表现得更像一个浏览器,也就是需要可以回退历史记录,因此需要覆盖系统的回退键 goBack,goForward可向前向后浏览历史页面

Android,WebView,JS

例子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("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);
    }
}

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方法。

Android,WebView,JS

那么这个showToast方法做了什么呢?

Android,WebView,JS

可以看到control.showToast,这个是什么我们后面再说,下面看我们Android工程中的java代码。

编写布局文件activity_main.xml

布局的内容很简单,就是嵌套一个WebView控件

Android,WebView,JS

编写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);
     }
}

上面的代码主要做了以下的步骤:

a) 获取webview控件

b) 获取webview的设置,将JavaScript设置为可用,打开JavaScript的通道

Android,WebView,JS

c) 在Android程序中建立接口 ,并编写相关逻辑

再去看之前js脚本中的那个showToast()方法

Android,WebView,JS

这里的control就是我们的那个interface,调用了interface的showToast方法,很明显这里是js调用了Android的代码,输出了一个Toast

Android,WebView,JS

可以看到这个interface我们给它取名叫control,最后通过loadUrl加载页面。

Android,WebView,JS

可以看到先显示一个toast,然后调用log()方法,log()方法里调用了js脚本的log()方法, js的log()方法做的事就是在控制台输出msg,这里明显是Android调用了js的方法。

Android,WebView,JS

d) 给webview添加我们自己编写的JavaScript接口

通过WebView的addJavascriptInterface方法去注入一个我们自己写的interface。

Android,WebView,JS

e) 使用webview控件加载我们之前编写的html文件

Android,WebView,JS

在真实手机上运行程序,在控制台成功输出内容:

Android,WebView,JS

这样我们就完成了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" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <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="http://yarin.javaeye.com";
                 }
                 else
                 {
                       alert("你选择了不去!");
                 }
           }

           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>

页面效果:

Android,WebView,JS

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控件,并进行相关的设置

Android,WebView,JS

5) 复写onKeyDown方法,当用户按返回键时,返回上一个加载的页面

Android,WebView,JS

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("输入网址错误,请重新输入");
         }
      }
   });
}

Android,WebView,JS

图1 dialog.html页面

Android,WebView,JS

图2 javascript的警告对话框

Android,WebView,JS

图3 javascript的confirm对话框

Android,WebView,JS

图4 javascript的prompt对话框

总结:在这个项目中,使用setWebChromeClient方法来为WebView设置一个WebChromeClient对象,来辅助WebView来处理Javascript的对话框等,图4是我们自定义的对话框,图2和图3我们都只需要监听按钮的点击事件,然后通过confirm和cancel方法将我们的操作传递给Javascript进行处理。当你在图1的界面,点击第一个按钮时,会打开图2的对话框,点击第二个按钮时,会打开图3的对话框,同时在这里点击确定,会跳转到另一个页面,当点击第三个按钮时,会打开图4对话框,并且可以输入内容。

转载请注明:Android开发中文站 » Android中使用WebView与JS交互全解析

您必须 登录 才能发表评论!