Android控件十五:WebView使用(一):Native与JS相互调用

原文:https://blog.csdn.net/harvic880925/article/details/51464687

一、基本用法

1、加载在线URL
void loadUrl(String url)
如:
mWebView.loadUrl(“http://www.baidu.com”);
这个函数主要加载url所对应的网页地址,或者用于调用网页中的指定的JS方法,但有一点必须注意的是:loadUrl()必须在主线程中执行!!!否则就会报错!!!。
加载在线网页地址是会用到联网permission权限的,所以需要在AndroidManifest.xml中写入下面代码申请权限:

注意:上种情况默认使用系统浏览器打开,要使用webview打开,需要设置WebViewClient实例
mWebView.setWebViewClient(new WebViewClient());

2、加载本地URL
本地web文件路径:assets/xx.html
如:
mWebView.loadUrl(“file:///android_asset/web.html”);

总结:

  1. 如果是在线网址记得添加网络访问权限
  2. 在线网址中,如果要使用webview打开,记得设置WebViewClient
  3. 打开本地html文件时,是不需要设置WebViewClient,对应的asstes目录的url为:file:///android_asset/xxxxx

3、WebView基本设置
如果我们需要设置WebView的属性,是通过WebView.getSettings()获取设置WebView的WebSettings对象,然后调用WebSettings中的方法来实现的。
WebSettings的方法简介:

			/**
			* 是否支持缩放,配合方法setBuiltInZoomControls使用,默认true
			*/
			setSupportZoom(boolean support)
			 
			/**
			* 是否需要用户手势来播放Media,默认true
			*/
			setMediaPlaybackRequiresUserGesture(boolean require)
			 
			/**
			* 是否使用WebView内置的缩放组件,由浮动在窗口上的缩放控制和手势缩放控制组成,默认false
			*/
			setBuiltInZoomControls(boolean enabled)
			 
			/**
			* 是否显示窗口悬浮的缩放控制,默认true
			*/
			setDisplayZoomControls(boolean enabled)
			 
			/**
			* 是否允许访问WebView内部文件,默认true
			*/
			setAllowFileAccess(boolean allow)
			 
			/**
			* 是否允许获取WebView的内容URL ,可以让WebView访问ContentPrivider存储的内容。 默认true
			*/
			setAllowContentAccess(boolean allow)
			 
			/**
			* 是否启动概述模式浏览界面,当页面宽度超过WebView显示宽度时,缩小页面适应WebView。默认false
			*/
			setLoadWithOverviewMode(boolean overview)
			 
			/**
			* 是否保存表单数据,默认false
			*/
			setSaveFormData(boolean save)
			 
			/**
			* 设置页面文字缩放百分比,默认100%
			*/
			setTextZoom(int textZoom)
			 
			/**
			* 是否支持ViewPort的meta tag属性,如果页面有ViewPort meta tag 指定的宽度,则使用meta tag指定的值,否则默认使用宽屏的视图窗口
			*/
			setUseWideViewPort(boolean use)
			 
			 
			/**
			* 是否支持多窗口,如果设置为true ,WebChromeClient#onCreateWindow方法必须被主程序实现,默认false
			*/
			setSupportMultipleWindows(boolean support)
			 
			/**
			* 指定WebView的页面布局显示形式,调用该方法会引起页面重绘。默认LayoutAlgorithm#NARROW_COLUMNS
			*/
			setLayoutAlgorithm(LayoutAlgorithm l)
			 
			/**
			* 设置标准的字体族,默认”sans-serif”。font-family 规定元素的字体系列。
			* font-family 可以把多个字体名称作为一个“回退”系统来保存。如果浏览器不支持第一个字体,
			* 则会尝试下一个。也就是说,font-family 属性的值是用于某个元素的字体族名称或/及类族名称的一个
			* 优先表。浏览器会使用它可识别的第一个值。
			*/
			setStandardFontFamily(String font)
			 
			/**
			* 设置混合字体族。默认”monospace”
			*/
			setFixedFontFamily(String font)
			 
			/**
			* 设置SansSerif字体族。默认”sans-serif”
			*/
			setSansSerifFontFamily(String font)
			 
			/**
			* 设置SerifFont字体族,默认”sans-serif”
			*/
			setSerifFontFamily(String font)
			 
			/**
			* 设置CursiveFont字体族,默认”cursive”
			*/
			setCursiveFontFamily(String font)
			 
			/**
			* 设置FantasyFont字体族,默认”fantasy”
			*/
			setFantasyFontFamily(String font)
			 
			/**
			* 设置最小字体,默认8. 取值区间[1-72],超过范围,使用其上限值。
			*/
			setMinimumFontSize(int size)
			 
			/**
			* 设置最小逻辑字体,默认8. 取值区间[1-72],超过范围,使用其上限值。
			*/
			setMinimumLogicalFontSize(int size)
			 
			/**
			* 设置默认字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。
			*/
			setDefaultFontSize(int size)
			 
			/**
			* 设置默认填充字体大小,默认16,取值区间[1-72],超过范围,使用其上限值。
			*/
			setDefaultFixedFontSize(int size)
			 
			/**
			* 设置是否加载图片资源,注意:方法控制所有的资源图片显示,包括嵌入的本地图片资源。
			* 使用方法setBlockNetworkImage则只限制网络资源图片的显示。值设置为true后,
			* webview会自动加载网络图片。默认true
			*/
			setLoadsImagesAutomatically(boolean flag)
			 
			/**
			* 是否加载网络图片资源。注意如果getLoadsImagesAutomatically返回false,则该方法没有效果。
			* 如果使用setBlockNetworkLoads设置为false,该方法设置为false,也不会显示网络图片。
			* 当值从true改为false时。WebView会自动加载网络图片。
			*/
			setBlockNetworkImage(boolean flag)
			 
			/**
			* 设置是否加载网络资源。注意如果值从true切换为false后,WebView不会自动加载,
			* 除非调用WebView#reload().如果没有android.Manifest.permission#INTERNET权限,
			* 值设为false,则会抛出java.lang.SecurityException异常。
			* 默认值:有android.Manifest.permission#INTERNET权限时为false,其他为true。
			*/
			setBlockNetworkLoads(boolean flag)
			 
			/**
			* 设置是否允许执行JS。
			*/
			setJavaScriptEnabled(boolean flag)
			 
			/**
			* 是否允许Js访问任何来源的内容。包括访问file scheme的URLs。考虑到安全性,
			* 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的,
			* 不会受到影响。ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本
			* 以上默认为false
			*/
			setAllowUniversalAccessFromFileURLs(boolean flag)
			 
			 
			/**
			* 是否允许Js访问其他file scheme的URLs。包括访问file scheme的资源。考虑到安全性,
			* 限制Js访问范围默认禁用。注意:该方法只影响file scheme类型的资源,其他类型资源如图片类型的,
			* 不会受到影响。如果getAllowUniversalAccessFromFileURLs为true,则该方法被忽略。
			* ICE_CREAM_SANDWICH_MR1版本以及以下默认为true,JELLY_BEAN版本以上默认为false
			*/
			setAllowFileAccessFromFileURLs(boolean flag)
			 
			/**
			* 设置存储定位数据库的位置,考虑到位置权限和持久化Cache缓存,Application需要拥有指定路径的
			* write权限
			*/
			setGeolocationDatabasePath(String databasePath)
			 
			/**
			* 是否允许Cache,默认false。考虑需要存储缓存,应该为缓存指定存储路径setAppCachePath
			*/
			setAppCacheEnabled(boolean flag)
			 
			/**
			* 设置Cache API缓存路径。为了保证可以访问Cache,Application需要拥有指定路径的write权限。
			* 该方法应该只调用一次,多次调用自动忽略。
			*/
			setAppCachePath(String appCachePath)
			 
			/**
			* 是否允许数据库存储。默认false。查看setDatabasePath API 如何正确设置数据库存储。
			* 该设置拥有全局特性,同一进程所有WebView实例共用同一配置。注意:保证在同一进程的任一WebView
			* 加载页面之前修改该属性,因为在这之后设置WebView可能会忽略该配置
			*/
			setDatabaseEnabled(boolean flag)
			 
			/**
			* 是否存储页面DOM结构,默认false。
			*/
			setDomStorageEnabled(boolean flag)
			 
			/**
			* 是否允许定位,默认true。注意:为了保证定位可以使用,要保证以下几点:
			* Application 需要有android.Manifest.permission#ACCESS_COARSE_LOCATION的权限
			* Application 需要实现WebChromeClient#onGeolocationPermissionsShowPrompt的回调,
			* 接收Js定位请求访问地理位置的通知
			*/
			setGeolocationEnabled(boolean flag)
			 
			/**
			* 是否允许JS自动打开窗口。默认false
			*/
			setJavaScriptCanOpenWindowsAutomatically(boolean flag)
			 
			/**
			* 设置页面的编码格式,默认UTF-8
			*/
			setDefaultTextEncodingName(String encoding)
			 
			/**
			* 设置WebView代理,默认使用默认值
			*/
			setUserAgentString(String ua)
			 
			/**
			* 通知WebView是否需要设置一个节点获取焦点当
			* WebView#requestFocus(int,android.graphics.Rect)被调用的时候,默认true
			*/
			setNeedInitialFocus(boolean flag)
			 
			/**
			* 基于WebView导航的类型使用缓存:正常页面加载会加载缓存并按需判断内容是否需要重新验证。
			* 如果是页面返回,页面内容不会重新加载,直接从缓存中恢复。setCacheMode允许客户端根据指定的模式来
			* 使用缓存。
			* LOAD_DEFAULT 默认加载方式
			* LOAD_CACHE_ELSE_NETWORK 按网络情况使用缓存
			* LOAD_NO_CACHE 不使用缓存
			* LOAD_CACHE_ONLY 只使用缓存
			*/
			setCacheMode(int mode)
			 
			/**
			* 设置加载不安全资源的WebView加载行为。KITKAT版本以及以下默认为MIXED_CONTENT_ALWAYS_ALLOW方
			* 式,LOLLIPOP默认MIXED_CONTENT_NEVER_ALLOW。强烈建议:使用MIXED_CONTENT_NEVER_ALLOW
			*/
			setMixedContentMode(int mode)
		
		使用实例:
			示例1:在WebView中启用JavaScript:
				WebSettings webSettings = mWebView.getSettings();
				webSettings.setJavaScriptEnabled(true);
				
			示例2:设置缓存
				优先使用缓存
				webView.getSettings().setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK);
				不使用缓存:
				webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
			示例3:打开页面时, 自适应屏幕:
				WebSettings webSettings =   mWebView .getSettings();       
				webSettings.setUseWideViewPort(true);//设置此属性,可任意比例缩放
				webSettings.setLoadWithOverviewMode(true);
			示例4:使页面支持缩放:
				WebSettings webSettings = mWebView.getSettings();
				//开启javascript支持
				webSettings.setJavaScriptEnabled(true); 
				// 设置可以支持缩放
				webSettings.setSupportZoom(true);
				// 设置出现缩放工具
				webSettings.setBuiltInZoomControls(true);
				
			示例5:.如果webView中需要用户手动输入用户名、密码或其他,则webview必须设置支持获取手势焦点
				webview.requestFocusFromTouch();	

二、JS调用Java代码

1、概述
更多时候,网页中需要通过JS代码来调用本地的Android代码,比如H5页面需要判断当前用户是否登录等。
利用JS代码调用JAVA代码,主要是用到WebView下面的一个函数:
public void addJavascriptInterface(Object obj, String interfaceName)
这个函数有两个参数:
Object obj:interfaceName所绑定的对象
String interfaceName:所绑定的对象所对应的名称
它有意义就是向WebView注入一个interfaceName的对象,这个对象绑定的是obj对象,通过interfaceName就可以调用obj对象中的方法

2,实例

Android代码:

			public class MyActivity extends Activity {

				private WebView mWebView;
				private Button mBtn;
			 
				@Override
				public void onCreate(Bundle savedInstanceState) {
					super.onCreate(savedInstanceState);
					setContentView(R.layout.main);
			 
					mWebView = (WebView) findViewById(R.id.webview);
			 
					WebSettings webSettings = mWebView.getSettings();
					webSettings.setJavaScriptEnabled(true);
					//把MyActivity对象注入到WebView中,在WebView中的对象别名叫android
					mWebView.addJavascriptInterface(this, "android");
					mWebView.loadUrl("file:///android_asset/web.html");
				}
				
				//用于弹出MSG 
				public void toastMessage(String message) {
					Toast.makeText(getApplicationContext(), "通过Natvie传递的Toast:"+message, Toast.LENGTH_LONG).show();
				}
			}    

HTML代码:

			<!DOCTYPE html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">
				<title>Title</title>
				<h1>欢迎光临启舰的blog</h1>
				<input type="button" value="js调native" onclick="ok()">
			</head>
			<body>
				<script type="text/javascript">
					function ok() {
					android.toastMessage("哈哈,i m webview msg");
					}
				</script>
			</body>
			</html>

它的意义就是调用android对象里的toastMessage方法,这个android就是我们利用mWebView.addJavascriptInterface(this, “android”)注入到WebView的android,它所对应的对象就将MyActivity;所以在JS中,我们就可以通过android这个别名来调用MyActivity对象中的任何public方法。

3、addJavascriptInterface自定义作用对象
mWebView.addJavascriptInterface(this, “android”);我们直接通过this,把当前整个类作为对象传给WebView了,但这会有很大风险,因为我们这个类中可能会有各种函数,而这些函数是只有本地Native代码才会用到,WebView是根本用不到的。所以,一般而言,我们很少直接会传this,把整个类注入给WebView,而是单独写一个类专门用于JS交互,比如:

		public class MyActivity extends Activity {
			private WebView mWebView;
			private Button mBtn;
		 
			@Override
			public void onCreate(Bundle savedInstanceState) {
				super.onCreate(savedInstanceState);
				setContentView(R.layout.main);
		 
				mWebView = (WebView) findViewById(R.id.webview);
				mBtn = (Button) findViewById(R.id.btn);
		 
				WebSettings webSettings = mWebView.getSettings();
				webSettings.setJavaScriptEnabled(true);
				mWebView.addJavascriptInterface(new JSBridge(), "android");
				mWebView.loadUrl("file:///android_asset/web.html");
			}
		 
			public class JSBridge{
				public void toastMessage(String message) {
					Toast.makeText(getApplicationContext(), "通过Natvie传递的Toast:"+message, Toast.LENGTH_LONG).show();
				}
			}
		}    
		
		JS调用时与上面一样:
			android.toastMessage("哈哈,i m webview msg");

4、JavascriptInterface注解
为了解决addJavascriptInterface()函数的安全问题,在android:targetSdkVersion数值为17(Android4.2)及以上的APP中,JS只能访问带有 @JavascriptInterface注解的Java函数,所以如果你的android:targetSdkVersion是17+,与JS交互的Native函数中,必须添加JavascriptInterface注解,不然无效,
比如:

			public class JSBridge {
				@JavascriptInterface
				public void toastMessage(String message) {
					Toast.makeText(getApplicationContext(), "通过Natvie传递的Toast:" + message, Toast.LENGTH_LONG).show();
				}
			}

三、JAVA调用JS代码

1、JAVA调用JS代码(调用webview中的JavaScript求和函数)
html代码:

			<!DOCTYPE html>
			<html lang="en">
			<head>
				<meta charset="UTF-8">
				<title>Title</title>
				<h1 id="h">欢迎光临启舰的blog</h1>
				<input type="button" value="js调native" onclick="ok()">
			</head>
			<body>
				<script type="text/javascript">
				function sum(i,m)
					{
					document.getElementById("h").innerHTML= (i+m);
					}
				</script>
			</body>
			</html>

JAVA的调用代码:

			public class MyActivity extends Activity {
			 
				private WebView mWebView;
				private Button mBtn;
			 
				@Override
				public void onCreate(Bundle savedInstanceState) {
					super.onCreate(savedInstanceState);
					setContentView(R.layout.main);
			 
					mWebView = (WebView) findViewById(R.id.webview);
					mBtn = (Button) findViewById(R.id.btn);
			 
					WebSettings webSettings = mWebView.getSettings();
					webSettings.setJavaScriptEnabled(true);
					mWebView.loadUrl("file:///android_asset/web.html");
			 
					mBtn.setOnClickListener(new View.OnClickListener() {
						@Override
						public void onClick(View v) {
							mWebView.loadUrl("javascript:sum(3,8)");
						}
					});
				}
			}       

2、JAVA中如何得到JS中的返回值
Android4.4以前:
(1).Java调用js代码

				webView.addJavascriptInterface(this, "android");
				mWebView.loadUrl("javascript:sum(3,8)");
			注意,这里通过addJavascriptInterface将MyActiviy所对应的对象注入到WebView中了。

(2).js函数处理,并将结果通过调用java方法返回

				function sum(i,m){
				var result = i+m;
				document.getElementById("h").innerHTML= result;
				android.onSumResult(result)
				}

(3).Java在回调方法中获取js函数返回值

				public void onSumResult(int result) {
				 Log.i(LOGTAG, "onSumResult result=" + result);
				}

Android4.4之后:先写一个具有返回值的js方法

				function getGreetings() {
				return 1; 
				}

java代码时用evaluateJavascript方法调用:

				private void testEvaluateJavascript(WebView webView) {
				  webView.evaluateJavascript("getGreetings()", new ValueCallback() {
					  @Override
					  public void onReceiveValue(String value) {
						  Log.i(LOGTAG, "onReceiveValue value=" + value);
					  }
				  });
				}

注意:
上面限定了结果返回结果为String,对于简单的类型会尝试转换成字符串返回,对于复杂的数据类型,建议以字符串形式的json返回。evaluateJavascript方法必须在UI线程(主线程)调用,因此onReceiveValue也执行在主线程。

上一篇:WebView浏览器组件


下一篇:Go语言:TTD(测试驱动开发)