您的位置:首页 > 移动开发 > Android开发

WebView的使用之Android与JS通过WebView互调方法

2018-01-11 17:24 525 查看
一、概述:

Android与JS通过WebView实现交互,实际上是:

Android调用JS的代码;

JS调用Android的代码;

二者互调的纽带就是WebView。

Android调用JS代码的方法有以下几种:

通过WebView的loadUrl();

通过WebView的evaluateJavascript();

JS调用Android代码的方法要多点,有以下3种:

- 通过WebView的 addJavascriptInterface() 进行对象映射;

- 通过WebViewClient的 shouldOverrideUrlLoading () 方法回调拦截url;

- 通过WebChromeClient的 onJsAlert()、onJsConfirm()、onJsPrompt()方法回调拦截JS对话框alert()、confirm()、prompt()消息.

-

二、Android通过WebView调用 JS 方法

1、通过WebView的loadUrl()使用详情:

Android中激发点击事件,即调用WebView JS(文本名为webjs)中的方法;

——为了示例方便及响应速度,这里采用Andorid调用本地JS代码进行解释。实际开发中,Andorid极可能调用本地Html文件中的JS代码,也可能调用网络加载的Html页面中的JS代码。

步骤:

A、将需要调用的JS代码的HTML文件放到src/main/assets文件夹里:



webjs.html文件中代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<h4>JS与Android(Java)的交互</h4>
<input type="button" value="js调Android" onclick="ok()">
<script type="text/javascript">
function ok() {
android.webMessage("JS与Android(Java)的交互");
}
function javaCallJavascriptNoParam() {
alert("Android中的Java通过WebView调用了javaScript的无参构造");
document.getElementById("javacalljs").innerHTML = "Android中的Java通过WebView调用了javaScript的无参构造<br\>";
}
function javaCallJavascript(param) {
alert(param);
document.getElementById("javacalljs").innerHTML = param;
}
</script>
</head>
<body>
<div id="javacalljs"></div>
<br/><br/>
</body>
</html>


B、在Activity中设置WebView的常用属性:

1)、让网页显示在WebView中而不是用浏览器打开

mWeb.setWebViewClient(new WebViewClient());


2)、设置启动概述模式浏览界面、使用宽屏的视图窗口。

WebSettings setting = mWeb.getSettings();
setting.setUseWideViewPort(true);
setting.setLoadWithOverviewMode(true);


3)、允许调用Js的权限:

setting.setJavaScriptEnabled(true);
setting.setJavaScriptCanOpenWindowsAutomatically(true);


B、在Activity中加载App内置的html文件:

String URL = "file:///android_asset/webjs.html";
mWeb.loadUrl(URL);


C、在Activity的Java代码中调用html文件中的javascript代码:

@OnClick(R.id.tvLoadWeb)
public void onViewClicked() {
mWeb.loadUrl("javascript:javaCallJavascript('Android中的Java通过WebView调用了javaScript的有参构造方法')");
}


点击按钮,弹出的弹框效果如下:



这里要注意了!注意了!!注意了!!!重要的事情说三遍:

1、javascript中没有Java里面的多态和重载的概念,也就是说不能出现相同名字的方法,哪怕是后面的参数不一样(最鲜明的就是有参和无参)也不行。就如上面html文件里面的方法function javaCallJavascriptNoParam() {}和function javaCallJavascript(param) {},如果我们都用同一个方法名,有时候调用就会报错:Undefined;

2、在WebView调用javascript方法的loadUrl方法里面:

mWeb.loadUrl("javascript:javaCallJavascript('Android中的Java通过WebView调用了javaScript的有参构造方法')");


中,要以”javascript”开头,有部分博客上面写的是以html的文件名开头,如本例中html的文件名为:“webjs”,他们就写成:

mWeb.loadUrl("webjs:javaCallJavascript(Android中的Java通过WebView调用了javaScript的有参构造方法')");


博主表示这样写也调用成功,但是我在学习的时候没有一次是调用成功的,不知道是我没学到家还是怎么的,总之调用起来各种不顺利。但可以确定的一点是:用前一种方法是肯定可以成功调用的。

3、有参无参其实都一样,就传不传参数而已,这个不多说。

三、JS通过WebView调用Java的方法

调用步骤:

1、创建html文件并放在src/main/assets文件夹下:



webjs.html文件代码如下:

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Title</title>
<h4>JS与Android(Java)的交互</h4>
<input type="button" value="js调Android" onclick="ok()">
<script type="text/javascript">
function javaCallJsNoArgs() {
document.getElementById("content1").innerHTML += "js调用java,Java再回调js的无参方法<br\>"
}
function javaCallJsExistArgs(args) {
document.getElementById("content2").innerHTML += "js调用java,Java再回调js的无参方法,参数是:" + args + "<br\>";
}
</script>
</head>
<body>
<div id="javacalljs"></div>
<br/><br/>
JS与Android(Java)的交互<br/><br/><br/>
<input type="button" value="JS调Java无参方法方法然后再回调"
onclick="js_call_java.javaCallJsMethod1()"/><br/>
<div id="content1"></div>
<br/><br/>
<input type="button" value="JS调Java有参方法方法然后再回调"
onclick="js_call_java.javaCallJsMethod2()"/><br/>
<div id="content2"></div>
</body>
</html>


2、在Activity的xml文件中布局WebView控件,并设置对应的Java和Js互调的属性:

WebSettings setting = mWvJsCallJava.getSettings();
setting.setJavaScriptEnabled(true);
/下面单个属性的作用前面已经介绍过了,不再重复。
setting.setJavaScriptCanOpenWindowsAutomatically(true);
setting.setUseWideViewPort(true);
setting.setLoadWithOverviewMode(true);
//设置编码格式
setting.setDefaultTextEncodingName("utf-8");
WebViewUtils.webViewSetting(setting, true, false, getApplicationContext());
WebViewUtils.setViewClient(mWvJsCallJava);
WebViewUtils.setChromeClien(mWvJsCallJava, getApplicationContext());
//添加一个对象,让js对象可以访问该对象的方法
mWvJsCallJava.addJavascriptInterface(new Javascript(), "js_call_java");


上面的Javascript.class文件代码如下:

final class Javascript{
public Javascript(){

}
@JavascriptInterface
public void javaCallJsMethod1() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mWvJsCallJava.loadUrl("javascript:javaCallJsNoArgs()");
}
});
}
@JavascriptInterface
public void javaCallJsMethod2() {
runOnUiThread(new Runnable() {
@Override
public void run() {
mWvJsCallJava.loadUrl("javascript:javaCallJsExistArgs('我是皓月')");
}
});
}
}


addJavascriptInterface()方法里面参数”js_call_java”的作用:

我的理解是:

他是WebView与JS交互时的一个标识符,点击WebView中的按钮,按钮就会通过”js_call_java”标识符调用Java代码中标识符后面的方法。比如:

<input type="button" value="JS调Java有参方法方法然后再回调"
onclick="js_call_java.javaCallJsMethod2()"/>


点击这个button后,系统就会通过WebView根据标识符”js_call_java”去调用他后面对应的方法。这里,标识符”js_call_java”后面的方法是:javaCallJsMethod2(),那么他就会调用Javascript对象里面的javaCallJsMethod2()方法。

上面WebViewUtils.class文件的代码在文末贴出。

3、加载html文件

String URL = "file:///android_asset/webjs.html";
mWvJsCallJava.loadUrl(URL);


这样就完成了JS对Java代码的调用。我们还可以在Java代码里面继续调用js方法,就像上面那样:

mWvJsCallJava.loadUrl("javascript:javaCallJsExistArgs('我是皓月')");


JS调用Java代码的效果如下:



这里Java代码调用了js代码里面的javaCallJsExistArgs()有参方法。另一个也是如此。

至此,JS与Java的交互告一段落。

WebViewUtils.class文件的代码如下:

public class WebViewUtils {
public static void webViewSetting(WebSettings setting, boolean setTure, boolean setFalse, Context context) {
/////////////////////////////////////////////////////////////////////////////////////////
/**是否允许数据库存储,需要读写权限,默认false**/
/**查看setDatabasePath API 如何正确设置数据库存储。
* 该设置拥有全局特性,同一进程所有WebView实例共用同一配置。注意:保证在同一进程的任一WebView
* 加载页面之前修改该属性,因为在这之后设置WebView可能会忽略该配置 **/
setting.setDatabaseEnabled(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否设置缓存,必需设置有效的缓存路径才能生效,默认值 false**/
setting.setAppCacheEnabled(true);
setting.setAppCachePath(context.getCacheDir().getAbsolutePath());
/////////////////////////////////////////////////////////////////////////////////////////
/**是否允许定位,默认true。
* 注意:为了保证定位可以使用,要保证以下几点:
* 1、Application需要有android.Manifest.permission#ACCESS_COARSE_LOCATION定位权限
* 2、Application需要实现WebChromeClient#onGeolocationPermissionsShowPrompt的监听回调,
*    接收Js定位请求访问地理位置的通知 **/
setting.setGeolocationEnabled(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否保存表单数据,默认false**/
setting.setSaveFormData(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否启动概述模式浏览界面,当页面宽度超过WebView显示宽度时,缩小页面适应WebView。默认false
* 需要和setUseWideViewPort()配合使用才生效**/
setting.setLoadWithOverviewMode(setTure);
/**是否支持ViewPort的meta tag属性,如果页面有ViewPort meta tag 指定的宽度,
* 则使用meta tag指定的值,否则默认使用宽屏的视图窗口(横竖屏时的宽度)**/
setting.setUseWideViewPort(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**通知WebView是否需要设置一个节点获取焦点,当WebView#requestFocus(int,android.graphics.Rect)
* 被调用的时候,默认true **/
setting.setNeedInitialFocus(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**布局算法**/
setting.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.NORMAL);
/////////////////////////////////////////////////////////////////////////////////////////
/**设置是否(支持)允许执行JS方法**/
setting.setJavaScriptEnabled(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否支持多窗口,如果设置为true ,WebChromeClient的onCreateWindow方法必须被主程序实现,默认false**/
setting.setSupportMultipleWindows(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否允许JS自动打开窗口。默认false **/
setting.setJavaScriptCanOpenWindowsAutomatically(setFalse);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否允许获取WebView的内容URL,可以让WebView访问ContentPrivider存储的内容.默认true**/
setting.setAllowContentAccess(setTure);
/**是否允许访问WebView内部文件,默认true, 不知道什么意思**/
setting.setAllowFileAccess(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否允许通过file url加载的Javascript读取本地文件,默认值 false**/
setting.setAllowFileAccessFromFileURLs(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false**/
setting.setAllowUniversalAccessFromFileURLs(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////资源加载
/**是否自动加载图片,包括从网络获取的图片、app内置的图片以及从Sdcard获取的图片**/
setting.setLoadsImagesAutomatically(setTure);
/**是否自动加载网络图片**/
setting.setBlockNetworkImage(setTure);
/**是否加载网络资源**/
setting.setBlockNetworkLoads(setTure);
/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////缩放(zoom)
/**是否支持缩放,配合setBuiltInZoomControls使用,默认true **/
setting.setSupportZoom(setTure);
/**是否使用WebView内置的缩放组件,由浮动在窗口上的缩放控制和手势缩放控制组成,默认false**/
setting.setBuiltInZoomControls(setFalse);
/**是是否显示内置缩放控件(有点像进度条,左边是“-”,右边是“+”)**/
setting.setDisplayZoomControls(setFalse);
/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////默认文本编码,默认值 "UTF-8"
/**设置页面的编码格式,默认UTF-8 **/
setting.setDefaultTextEncodingName("utf-8");
/////////////////////////////////////////////////////////////////////////////////////////
/**设置默认字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。
* 另外WebView各种字体的大小的设置有一系列的方法:setXxxxFontSize(int size)**/
setting.setDefaultFontSize(16);
/**默认等宽字体尺寸,默认值16**/
setting.setDefaultFixedFontSize(16);
/**最小文字尺寸,默认值 8**/
setting.setMinimumFontSize(8);
/**最小文字逻辑尺寸,默认值 8**/
setting.setMinimumLogicalFontSize(8);
/**文字缩放百分比,默认值 100**/
setting.setTextZoom(100);
/////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////设置字体
/**是否支持多窗口如果设置为true,WebChromeClient的onCreateWindow方法必须被主程序实现,默认false
* 另外WebView各种字体的设置有一系列的方法:setXxxxFontFamily(String font)**/
setting.setStandardFontFamily("sans-serif");
/**衬线字体,默认值 "serif"**/
setting.setSerifFontFamily("serif");
/**无衬线字体,默认值 "sans-serif"**/
setting.setSansSerifFontFamily("sans-serif");
/**等宽字体,默认值 "monospace"**/
setting.setFixedFontFamily("monospace");
/**手写体(草书),默认值 "cursive"**/
setting.setCursiveFontFamily("cursive");
/**幻想体,默认值 "fantasy"**/
setting.setFantasyFontFamily("fantasy");
/////////////////////////////////////////////////////////////////////////////////////////
/**存储(storage),启用HTML5 DOM storage API,默认值 false*/
setting.setDomStorageEnabled(true);
/////////////////////////////////////////////////////////////////////////////////////////
/**是否需要用户手势来播放Media,默认true**/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
setting.setMediaPlaybackRequiresUserGesture(setTure);
}
/////////////////////////////////////////////////////////////////////////////////////////
/**设置加载不安全资源的WebView加载行为。KITKAT版本以及以下默认为MIXED_CONTENT_ALWAYS_ALLOW
* 方式,LOLLIPOP默认MIXED_CONTENT_NEVER_ALLOW。强烈建议:使用MIXED_CONTENT_NEVER_ALLOW **/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setting.setMixedContentMode(WebSettings.MIXED_CONTENT_ALWAYS_ALLOW);
}
/////////////////////////////////////////////////////////////////////////////////////////
/**是否在离开屏幕时光栅化(会增加内存消耗),默认值 false**/
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
setting.setOffscreenPreRaster(false);
}
/////////////////////////////////////////////////////////////////////////////////////////
/**根据cache-control决定是否从网络上取数据
* LOAD_DEFAULT 默认加载方式
* LOAD_CACHE_ELSE_NETWORK 按网络情况使用缓存
* LOAD_NO_CACHE 不使用缓存
* LOAD_CACHE_ONLY 只使用缓存 **/
if (isNetworkAvailable(context)) {
//有网络,从网络获取。
setting.setCacheMode(WebSettings.LOAD_DEFAULT);
} else {
// 没网,离线加载缓存(即使已经过期)
setting.setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
}
}

public static void setViewClient(WebView mWeb) {
/*****作用:让HTML网页显示在显示在WebView中而不是用浏览器打开**/
mWeb.setWebViewClient(new WebViewClient() {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
//开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
super.onPageStarted(view, url, favicon);
}

@Override
public void onPageFinished(WebView view, String url) {
//在页面加载结束时调用。我们可以关闭loading 条,切换程序动作。
super.onPageFinished(view, url);
}

@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
//使得打开网页时不调用系统浏览器, 而是在本WebView中显示
view.loadUrl(url);
return super.shouldOverrideUrlLoading(view, url);
}

@Override
public boolean shouldOverrideUrlLoading(WebView view, WebResourceRequest request) {
return super.shouldOverrideUrlLoading(view, request);
}

@Override
public void onLoadResource(WebView view, String url) {
//在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
super.onLoadResource(view, url);
}

@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
//加载页面的服务器出现错误时(如404)调用。
switch (errorCode) {
//HttpStatus.SC_NOT_FOUND
case 404:
break;
default:
break;
}
super.onReceivedError(view, errorCode, description, failingUrl);
}

@Override
public void onReceivedSslError(WebView view, SslErrorHandler handler, SslError error) {
//专用于https请求
super.onReceivedSslError(view, handler, error);
}
});
}

public static void setChromeClien(WebView mWeb, final Context context) {
mWeb.setWebChromeClient(new WebChromeClient() {
@Override
public void onProgressChanged(WebView view, int newProgress) {
//网页的加载进度:newProgress即是加载进度百分比:0<=newProgress<=100;
super.onProgressChanged(view, newProgress);
}

@Override
public void onReceivedTitle(WebView view, String title) {
//要加载的网页的标题,比如http://www.baidu.com的标题:百度;http://www.ifeng.com的标题:凤凰网。
super.onReceivedTitle(view, title);
}

@Override
public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
//允许弹出javascript的警告框,message就是警告框的内容。
return super.onJsAlert(view, url, message, result);
}

@Override
public boolean onJsConfirm(WebView view, String url, String message, final JsResult result) {
//允许弹出javascript的确认框,message就是确认信息。
new AlertDialog.Builder(context)
.setTitle("信息确认")
.setMessage(message)
.setPositiveButton("确定", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.confirm();
}
})
.setNegativeButton("取消", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
result.cancel();
}
})
.setCancelable(false)
.show();
// 返回布尔值:判断点击时确认还是取消
// true表示点击了确认;false表示点击了取消;
return super.onJsConfirm(view, url, message, result);
}

@Override
public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult result) {
//允许弹出javascript输入框
EditText et = new EditText(context);
AlertDialog.Builder dialog = new AlertDialog.Builder(context);
dialog.setTitle("输入信息").setView(et)
.setPositiveButton("Sure", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//返回输入框中的值,
}
})
.setNegativeButton("Cancel", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
//返回null。
}
}).setCancelable(false).show();
return super.onJsPrompt(view, url, message, defaultValue, result);
}
});
}

public static void clearCache(WebView mWeb) {
//清除网页访问留下的缓存,这个方法是针对整个应用程序.
mWeb.clearCache(true);
//清除当前webview访问的历史所有记录
mWeb.clearHistory();
//这个api仅仅清除自动完成填充的表单数据,并不会清除WebView存储到本地的数据
mWeb.clearFormData();
}

public static boolean isNetworkAvailable(Context context) {
// 获取手机所有连接管理对象(包括对wi-fi,net等连接的管理)
try {
ConnectivityManager connectivity = (ConnectivityManager) context
.getSystemService(Context.CONNECTIVITY_SERVICE);
if (connectivity != null) {
// 获取网络连接管理的对象
NetworkInfo info = connectivity.getActiveNetworkInfo();
if (info != null && info.isConnected()) {
// 判断当前网络是否已经连接
if (info.getState() == NetworkInfo.State.CONNECTED) {
return true;
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}

public static Bitmap getLocalBitmap(String URL) {
try {
FileInputStream input = new FileInputStream(URL);
Log.i("TAG", "onViewClicked: input is Empty?:" + (input == null));
return BitmapFactory.decodeStream(input);
} catch (Exception e) {
Log.i("TAG", "onViewClicked: 抛异常:" + e);
return null;
}
}
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: