JavaScript在Web开发中非常有用,而现在越来越多的App界面也由Javascript来创建,我们需要解决一个问题:java与javaScript怎么进行交互呢?
例如,我们可以在html中创建一个按钮,为按钮事件添加一个界面。然后你可以使用html按钮跳转到另一个Activity中。
(1)本地Html文件
添加一个新的Assert文件夹
右键点击“ App ”module>new>folder>assert folder,然后将为您创建一个Assert Folder。
创建一个html文件
之后,通过右键单击Assert Folder>new >file创建一个html文件。将html文件命名为“sample.html”。然后按照下面的源代码添加html文件中的按钮并创建javascript界面。
<html>
<head>
<style>
body{
}
input{
width: 300px;
padding:10px;
}
div#content{
padding:20px;
}
</style>
<script type="text/javascript">
function showToastA(toastmsg) {
InterfaceName.showToast(toastmsg);
}
function navigateToAnotherActivityA() {
InterfaceName.navigateToAnotherActivity();
}
</script>
</head>
<body>
<center>
<h3>Javascript bind to Android</h3>
<div>
<input type="button" value="Show Toast" onClick="showToastA('Message from Javascript')" /><br/><br/>
<input type="button" value="Go to Another Activity" onClick="navigateToAnotherActivityA()" />
</div>
</center>
</body>
</html>
创建第二个Activity
创建html文件后,转到项目中创建一个新的活动,所以稍后你将使用html按钮导航到这个Activity。
activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
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.questdot.javascriptwebviewexample.MainActivity">
<WebView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/webkit"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WebView browser;
browser=(WebView)findViewById(R.id.webkit);
browser.getSettings().setJavaScriptEnabled(true);
browser.addJavascriptInterface(new WebAppInterface(this), "InterfaceName");
browser.loadUrl("file:///android_asset/sample.html");
}
public class WebAppInterface {
Context mContext;
WebAppInterface(Context c) {
mContext = c;
}
@JavascriptInterface
public void showToast(String toast) {
Toast.makeText(mContext, toast, Toast.LENGTH_SHORT).show();
}
@JavascriptInterface
public void navigateToAnotherActivity(){
AlertDialog.Builder alertDialog = new AlertDialog.Builder(mContext);
alertDialog.setTitle("Alert Message");
alertDialog.setMessage("You want to Go another Activity?");
alertDialog.setPositiveButton("YES",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
Intent chnIntent = new Intent(MainActivity.this, SecondActivity.class);
startActivity(chnIntent);
}
});
alertDialog.setNegativeButton("NO",
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
// Showing Alert Message
alertDialog.show();
}
}
}
addJavascriptInterface
中的第二个参数“InterfaceName”应该与html文件中的相同,否则将报错。
还要注意的是4.2之前向webview注入的对象所暴露的接口toString没有注释语句@JavascriptInterface
,而4.2及以后的则多了注释语句@JavascriptInterface
经过查官方文档所知,因为这个接口允许JavaScript 控制宿主应用程序,这是个很强大的特性,但同时,在4.2的版本前存在重大安全隐患,因为javascript 可以使用反射访问注入webview的Java对象的public fields,在一个包含不信任内容的WebView中使用这个方法,会允许攻击者去篡改宿主应用程序,使用宿主应用程序的权限执行java代码。因此4.2以后,任何为js暴露的接口,都需要加。
(2)动态网页
图片点击
我们还可以对网页中的图片添加点击事件,获得该图片的信息,比如链接等。
主要代码如下:
// 注入js函数监听
private void addImageClickListner() {
// 这段js函数的功能就是,遍历所有的img结点,并添加onclick函数,函数的功能是在图片点击的时候调用本地java接口并传递url过去
contentWebView.loadUrl("javascript:(function(){" +
"var objs = document.getElementsByTagName(\"img\"); " +
"for(var i=0;i<objs.length;i++) " +
"{"
+ " objs[i].onclick=function() " +
" { "
+ " window.imagelistner.openImage(this.src); " +
" } " +
"}" +
"})()");
}
// js通信接口
public class JavascriptInterface {
private Context context;
public JavascriptInterface(Context context) {
this.context = context;
}
public void openImage(String img) {
System.out.println(img);
//
Intent intent = new Intent();
intent.putExtra("image", img);
intent.setClass(context, ShowWebImageActivity.class);
context.startActivity(intent);
System.out.println(img);
}
}
// 监听
private class MyWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
@Override
public void onPageFinished(WebView view, String url) {
view.getSettings().setJavaScriptEnabled(true);
super.onPageFinished(view, url);
// html加载完成之后,添加监听图片的点击js函数
addImageClickListner();
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
view.getSettings().setJavaScriptEnabled(true);
super.onPageStarted(view, url, favicon);
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
}
}
// 随便找了个带图片的网站
contentWebView.loadUrl("http://www.weim.me/12408.html");
// 添加js交互接口类,并起别名 imagelistner
contentWebView.addJavascriptInterface(new JavascriptInterface(this), "imagelistner");
contentWebView.setWebViewClient(new MyWebViewClient());
这种注入JS的方法基本可以说是放之四海而皆准的方法,我们还可以用此方法拦截按钮的点击事件。
按钮点击
我们以豆瓣登陆这个网页为例。
查看网页源代码,登陆按钮在这段代码中可以找到:
<form id="lzform" name="lzform" method="post" onsubmit="return validateForm(this);" action="https://accounts.douban.com/login">
<div style="display:none;">
<img src="https://www.douban.com/pics/blank.gif" onerror="document.lzform.action='https://accounts.douban.com/login'"/>
</div>
<input name="source" type="hidden" value="book"/>
<input name="redir" type="hidden" value="https://book.douban.com/subject/1041482/"/>
<div class="item-right">
<a href="?redir=https://book.douban.com/subject/1041482/&source=book&login_type=sms">手机验证码登录</a>
</div>
<div class="item">
<label>帐号</label>
<input id="email" name="form_email" type="text" class="basic-input"
maxlength="60" value="邮箱/手机号/用户名" tabindex="1"/>
</div>
<div class="item">
<label>密码</label>
<input id="password" name="form_password" type="password" class="basic-input" maxlength="20" tabindex="2"/>
</div>
<!-- tsYR99_fISw | 175.191.30.52 -->
<div class="item">
<label> </label>
<p class="remember">
<input type="checkbox" id="remember" name="remember" tabindex="4"/>
<label for="remember" class="remember">下次自动登录</label>
| <a href="https://accounts.douban.com/resetpassword">忘记密码了</a>
</p>
</div>
<div class="item">
<label> </label>
<input type="submit" value="登录" name="login" class="btn-submit" tabindex="5"/>
</div>
<div class="item item-3rd">
<label>
第三方登录:
</label>
<a target="_top" href="https://www.douban.com/accounts/connect/wechat/?from=book&redir=https%3A//book.douban.com/subject/1041482/" class="item-wechat"><img src="https://img3.doubanio.com/f/accounts/1b6cc3ca91f78cf47f41eafa91fbcd4918ae239c/pics/connect_wechat.png" title="微信"></a>
<a target="_top" href="https://www.douban.com/accounts/connect/sina_weibo/?from=book&redir=https%3A//book.douban.com/subject/1041482/&fallback=" class="item-weibo"><img src="https://img3.doubanio.com/f/accounts/e2f1d8c0ede93408b46cbbab4e613fb29ba94e35/pics/connect_sina_weibo.png" title="新浪微博"></a>
</div>
</form>
那么要怎么获取这个登录按钮的事件呢?关键代码如下:
//注入javascript到页面
webview1.setWebViewClient(new WebViewClient() {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
StringBuilder sb = new StringBuilder();
//获得网页中所有form节点的标签数组,这里获得数组的第一个form节点
//onsubmit 事件会在表单中的确认按钮被点击时发生。
sb.append("document.getElementsByTagName('form')[0].onsubmit = function () {");
//方法中的局部变量,用来存储用户名和密码
sb.append("var objPWD, objAccount;var str = '';");
//获得form节点下所有的input节点数组
sb.append("var inputs = document.getElementsByTagName('input');");
sb.append("for (var i = 0; i < inputs.length; i++) {");
//获得输入的密码
sb.append("if (inputs[i].type.toLowerCase() === 'password') {objPWD = inputs[i];}");
//获得输入的用户名
sb.append("else if (inputs[i].name.toLowerCase() === 'form_email') {objAccount = inputs[i];}");
sb.append("}");
sb.append("if (objAccount != null) {str += objAccount.value;}");
sb.append("if (objPWD != null) { str += ' , ' + objPWD.value;}");
sb.append("window.MYOBJECT.processHTML(str);");
sb.append("return true;");
sb.append("};");
view.loadUrl("javascript:" + sb.toString());
}
});
//创建一个由javascrip调用的类
class MyJavaScriptInterface
{
@JavascriptInterface
public void processHTML(String html)
{
//called by javascript
Log.d("henrytest", html);
AlertDialog.Builder builder = new AlertDialog.Builder(Main.this);
builder.setTitle("AlertDialog from app")
.setMessage(html)
.setPositiveButton(android.R.string.ok,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
// TODO Auto-generated method stub
}
})
.setCancelable(false).show();
}
}
//为javascript注册接口
webview1.getSettings().setJavaScriptEnabled(true);
webview1.addJavascriptInterface(new MyJavaScriptInterface(), "MYOBJECT");