之前有写过WebView的小demo,之后一直没有在项目中使用过网页开发,最近准备重新再看一下,记录一些基本的使用方法
相关链接:[Android使用JsBridge与JavaScript交互](https://www.jianshu.com/p/4ed80af1c103)
JavaScript调用Android方法
第一种是用webView的JavascriptInterface
注解进行对象映射
第二种是通过webViewClient的shouldOverrideUrlLoading()
方法拦截URL
第三种是通过WebChromeClient的onJsAlert()
、onJsConfirm()
、onJsPrompt()
来拦截JS对话框alert()
、confirm()
;
对象映射 比较简单,在Android的对象里面申明一些方法,暴露给JavaScript,传递这个对象给JavaScript,JavaScript就可以调用这些方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 webView = findViewById(R.id.wbView); webView.getSettings().setJavaScriptEnabled(true ); webView.loadUrl("file:///android_asset/test.html" ); webView.addJavascriptInterface(MainActivity.this , "activity" ); @JavascriptInterface public void showToast (String msg) { Toast.makeText(MainActivity.this , msg, Toast.LENGTH_SHORT).show(); }
HTML代码,放在assets文件下的HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 <html > <head > <title > js调用android原生代码</title > <meta http-equiv ="Content-Type" content ="text/html;charset=gb2312" > <meta id ="viewport" name ="viewport" content ="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,minimal-ui" > </head > <body > <br /> <li > <a onclick ="activity.showToast('你好呀');" > 点击调用Toast</a > </li > <br /> </body > </html >
调用方法返回数据 返回数据肯定和刚才那个是一样的,只是方法有返回值而已,只是测试的时候,因为我们不怎么会JavaScript呀,不知道HTML怎么使用这个JavaScript返回来的值,所以有些难搞;我试了两种方法证明的确拿到这个值了,其实怎么用我感觉也不用我们关心,我是找到一个类似Toast的JavaScript方法,用它展示获取到的返回值;
1 2 3 4 5 6 7 8 9 @JavascriptInterface public String getMsg () { return "Hello from Android" ; }
同样还是暴露一个方法给JavaScript,然后第一种方法是在HTML中获取到数据,然后调用HTML中的JavaScript显示
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <script > function toast (msg,duration ) { duration=isNaN (duration)?3000 :duration; var m = document .createElement('div' ); m.innerHTML = msg; m.style.cssText="width: 60%;min-width: 150px;opacity: 0.7;height: 30px;color: rgb(255, 255, 255);line-height: 30px;text-align: center;border-radius: 5px;position: fixed;top: 40%;left: 20%;z-index: 999999;background: rgb(0, 0, 0);font-size: 12px;" ; document .body.appendChild(m); setTimeout (function ( ) { var d = 0.5 ; m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in' ; m.style.opacity = '0' ; setTimeout (function ( ) { document .body.removeChild(m) }, d * 1000 ); }, duration); } </script > <li > <a onclick ="toast(window.activity.getMsg(),100)" > 点击获取MSG</a > </li >
第二种就是直接在JavaScript中获取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 <script > function toast (duration ) { var msg=window .activity.getMsg(); duration=isNaN (duration)?3000 :duration; var m = document .createElement('div' ); m.innerHTML = msg; m.style.cssText="width: 60%;min-width: 150px;opacity: 0.7;height: 30px;color: rgb(255, 255, 255);line-height: 30px;text-align: center;border-radius: 5px;position: fixed;top: 40%;left: 20%;z-index: 999999;background: rgb(0, 0, 0);font-size: 12px;" ; document .body.appendChild(m); setTimeout (function ( ) { var d = 0.5 ; m.style.webkitTransition = '-webkit-transform ' + d + 's ease-in, opacity ' + d + 's ease-in' ; m.style.opacity = '0' ; setTimeout (function ( ) { document .body.removeChild(m) }, d * 1000 ); }, duration); } </script > <li > <a onclick ="toast(100)" > 点击获取MSG</a > </li >
拦截URL JavaScript简单的发送一些消息
1 2 3 4 5 6 7 <script > function callAndroid ( ) { document .location = "js://webview?name=Tyhj" ; } </script > <li > <a onclick ="callAndroid()" > 点击测试拦截URL</a > </li >
Android通过webView.setWebViewClient()
重写shouldOverrideUrlLoading方法,拦截到URL,对协议进行解析,获取信息,从而响应JavaScript发送的数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 public static final String URL_SCHEME = "js" ;public static final String URL_AUTHORITY = "webview" ;webView.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading (WebView view, WebResourceRequest request) { Uri uri = request.getUrl(); if (URL_SCHEME.equals(uri.getScheme()) && URL_AUTHORITY.equals(uri.getAuthority())) { String name = uri.getQueryParameter("name" ); Toast.makeText(MainActivity.this , "JavaScript通过拦截调用Android代码" + name, Toast.LENGTH_SHORT).show(); return true ; } return super .shouldOverrideUrlLoading(view, request); } });
拦截JS对话框 正常网页是会弹出弹窗让我们点击操作或者输入操作返回一些值;现在我们监听到之后返回true而不是默认的方法,这时候就是拦截了这些对话框,网页就不会弹出来,但是也需要我们做响应的处理,并且返回值给JavaScript;
JavaScript简单的弹出对话窗
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <li > <a onclick ="alert('alert测试')" > 点击测alert</a > </li > <li > <a onclick ="showPrompt()" > 点击测prompt</a > </li > <li > <a onclick ="showConfirm()" > 点击测confirm</a > </li > <script > function showPrompt ( ) { var person = prompt("Please enter your name" , "Harry Potter" ); if (person != null ) { document .getElementById("demo" ).innerHTML = "Hello " + person + "! How are you today?" ; } } </script > <script > function showConfirm ( ) { var r=confirm("Press a button!" ); if (r==true ) { document .getElementById("demo" ).innerHTML ="you choose yes" ; }else { document .getElementById("demo" ).innerHTML ="you choose no" ; } } </script >
Android对这些方法进行拦截,并做出数据的展示,返回响应的值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onJsAlert (WebView view, String url, String message, JsResult result) { Toast.makeText(MainActivity.this , "onJsAlert:" + message, Toast.LENGTH_SHORT).show(); result.confirm(); return true ; } @Override public boolean onJsConfirm (WebView view, String url, String message, JsResult result) { Toast.makeText(MainActivity.this , "onJsConfirm:" + message, Toast.LENGTH_SHORT).show(); result.cancel(); return true ; } @Override public boolean onJsPrompt (WebView view, String url, String message, String defaultValue, JsPromptResult result) { Toast.makeText(MainActivity.this , "onJsPrompt:" + message, Toast.LENGTH_SHORT).show(); result.confirm("Tyhj" ); return true ; } });
Android调用JavaScript方法
第一种是webView的loadUrl()
方法,无法获取返回值
第二种是webView的evaluateJavascript()
方法,可以获取返回值
调用loadUrl方法 调用JavaScript的方法比较简单,直接调用就可以了,遇到一个问题是我在加载网页后直接调用JavaScript的方法发现一直不行,是网页还没有加载完成所以没法调用,监听一下网页加载完成再调用就好了
1 2 3 4 5 6 7 8 9 10 webView.loadUrl("file:///android_asset/test.html" ); webView.setWebViewClient(new WebViewClient() { @Override public void onPageFinished (WebView view, String url) { String msg = "呵呵呵" ; int duration = 1000 ; webView.loadUrl("javascript:toast('" + msg + "','" + duration + "')" ); } });
调用evaluateJavascript方法获取返回值 这个方法适用于Android4.4版本以上,以下版本其实也有一些应对的办法,可以自己找找
1 2 3 4 5 6 webView.evaluateJavascript("javascript:getMsg()" , new ValueCallback<String>() { @Override public void onReceiveValue (String value) { Toast.makeText(MainActivity.this ,value,Toast.LENGTH_SHORT).show(); } });
其实可以看出来,基本上只支持传递字符串而已,但是支持字符串,就意味着支持基本类型(自己强转一下)和Json数据了
Android注入js代码 有时候网页并不是我们定制的,里面没有我们需要的JavaScript代码,我们可以注入JavaScript代码进去;比如网页上的图片,我们可以提供查看和保存图片的功能,就需要注入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 public static final String GET_IMAGE_URL = "javascript:(function(){" + "var objs = document.getElementsByTagName(\"img\");" + "for(var i=0;i<objs.length;i++)" + "{" + "objs[i].onclick=function(){window.activity.imageClick(this.getAttribute(\"src\"));}" + "}" + "})()" ; webView.loadUrl("file:///android_asset/test.html" ); webView.addJavascriptInterface(MainActivity.this , "activity" ); webView.loadUrl(GET_IMAGE_URL); @JavascriptInterface public void imageClick (String imgUrl) { Toast.makeText(MainActivity.this , imgUrl, Toast.LENGTH_SHORT).show(); }
WebView长按事件 网页中相应Android的长按事件也是经常用到的,比如刚才的点击一般是查看图片,长按保存图片或者其他操作;其实就是设置WebView的长按事件,然后通过WebView的getHitTestResult()的函数可以获取点击页面元素的类型,然后,我们再根据类型进行相应的处理,还是以图片为例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 webView.setOnLongClickListener(new View.OnLongClickListener() { @Override public boolean onLongClick (View v) { final WebView.HitTestResult hitTestResult = webView.getHitTestResult(); if (hitTestResult.getType() == WebView.HitTestResult.IMAGE_TYPE || hitTestResult.getType() == WebView.HitTestResult.SRC_IMAGE_ANCHOR_TYPE) { String picUrl = hitTestResult.getExtra(); Toast.makeText(MainActivity.this , "长按获取到图片地址:" + picUrl, Toast.LENGTH_SHORT).show(); return true ; } return false ; } });
监听图片选择 这个是我随意加的,因为我们都可以互相调用了,那做什么都应该是没问题的,只是看见webView有一个setWebChromeClient
方法里面可以监听到图片选择,可以响应一下;就是监听到图片选择以后,调用系统方法选择图片,返回给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 ValueCallback<Uri[]> mUploadMessageArray; int RESULT_CODE = 0 ;webView.setWebChromeClient(new WebChromeClient() { @Override public boolean onShowFileChooser (WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) { mUploadMessageArray = filePathCallback; Intent chooserIntent = new Intent(Intent.ACTION_GET_CONTENT); chooserIntent.setType("image/*" ); startActivityForResult(chooserIntent, RESULT_CODE); return true ; } }); @Override protected void onActivityResult (int requestCode, int resultCode, @Nullable Intent data) { if (requestCode == RESULT_CODE) { if (mUploadMessageArray != null ) { Uri result = (data == null || resultCode != RESULT_OK ? null : data.getData()); mUploadMessageArray.onReceiveValue(new Uri[]{result}); mUploadMessageArray = null ; } } }
然后HTML里面就是简单的选择图片,好像都没有涉及到JavaScript
1 2 3 <p > <input type ="file" value ="打开文件" /> </p >
项目源码:https://github.com/tyhjh/WebViewH.git