Android WebView 的使用
WebView 是 Android 中一个非常实用的组件,它内置了WebKit引擎,WebKit是一个开源的浏览器引擎,Chrome浏览器也是基于它,所以我们可以把WebView当做一个轻量级的浏览器使用。 WebView 可以使得网页轻松的内嵌到 app 里,还可以直接跟 js 相互调用。
1、添加网络权限
<uses-permission android:name="android.permission.INTERNET" />
2、WebSettings 对访问页面进行设置。
WebView mWebView = new WebView(this);WebSettings webSettings = mWebView .getSettings(); // 支持获取手势焦点,输入用户名、密码或其他mWebView.requestFocusFromTouch();webSettings.setJavaScriptEnabled(true); // 支持jswebSettings.setUseWideViewPort(true); // 将图片调整到适合 webview 的大小webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小webSettings.setSupportZoom(true); // 支持缩放,默认为 true。是下面那个的前提。webSettings.setBuiltInZoomControls(true); // 设置内置的缩放控件。webSettings.setDisplayZoomControls(false); // 隐藏原生的缩放控件webSettings.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); // 支持内容重新布局webSettings.supportMultipleWindows(); // 多窗口webSettings.setAllowFileAccess(true); // 设置可以访问文件webSettings.setNeedInitialFocus(true); // 当 webview 调用 requestFocus 时为 webview 设置节点webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 支持通过 JS 打开新窗口webSettings.setLoadsImagesAutomatically(true); // 支持自动加载图片webSettings.setDefaultTextEncodingName("utf-8"); // 设置编码格式webSettings.setDatabaseEnabled(true); // 设置支持本地存储String path = getActivity().getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath(); // 取得缓存路径webSettings.setDatabasePath(path); // 设置缓存路径webSettings.setDomStorageEnabled(true); // 设置支持DomStoragewebSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 设置存储模式//webSettings。setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK) ;//关闭webview中缓存webSettings.setAppCacheEnabled(true); // 设置支持缓存mWebView.requestFocus();
3、页面加载方式
// 加载一个网页:mWebView.loadUrl("https://www.baidu.com/");// 加载 apk assets目录文件中的一个 html 页面mWebView.loadUrl("file:///android_asset/test.html");// 加载手机本地的一个 html 页面的方法:mWebView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");
4、WebView 的两个重要方法 WebViewClient 和 WebChromeClient
WebViewClient 就是帮助 WebView 处理各种通知、请求事件的。
// 打开网页时不调用系统浏览器,而是在本 WebView 中显示:mWebView.setWebViewClient(new WebViewClient(){@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {view.loadUrl(url);return true;}});// 将上面定义的 webviewclinet 设置给 webviewmWebView.setWebViewClient(webViewClient);
下面介绍 WebView 的一些事件:
WebViewClient mWebViewClient = new WebViewClient(){//在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。//比如获取url,查看url.contains(“add”),进行添加操作shouldOverrideUrlLoading(WebView view, String url);//重写此方法才能够处理在浏览器中的按键事件。shouldOverrideKeyEvent(WebView view, KeyEvent event);//这个事件就是开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。onPageStarted(WebView view, String url, Bitmap favicon) ;//在页面加载结束时调用。同样道理,我们可以关闭loading 条,切换程序动作。onPageFinished(WebView view, String url);// 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。onLoadResource(WebView view, String url) ;// (报告错误信息)onReceivedError(WebView view, int errorCode, String description, String failingUrl);//(更新历史记录)doUpdateVisitedHistory(WebView view, String url, boolean isReload);//(应用程序重新请求网页数据)onFormResubmission(WebView view, Message dontResend, Message resend);//(获取返回信息授权请求)onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host,String realm);//重写此方法可以让webview处理https请求。onReceivedSslError(WebView view, SslErrorHandler handler, SslError error);// (WebView发生改变时调用)onScaleChanged(WebView view, float oldScale, float newScale);//(Key事件未被加载时调用)onUnhandledKeyEvent(WebView view, KeyEvent event);}
WebChromeClient 是辅助 WebView 处理 Javascript 的对话框,网站图标,网站 title,加载进度等。
WebChromeClient mWebChromeClient = new WebChromeClient() {// 获得网页的加载进度,显示在右上角的 TextView 控件中@Overridepublic void onProgressChanged(WebView view, int newProgress) {if (newProgress < 100) {String progress = newProgress + "%";} else {}}// 获取 Web 页中的 title 用来设置自己界面中的 title// 当加载出错的时候,比如无网络,这时 onReceiveTitle 中获取的标题为 找不到该网页,// 因此建议当触发 onReceiveError 时,不要使用获取到的 title@Overridepublic void onReceivedTitle(WebView view, String title) {MainActivity.this.setTitle(title);}@Overridepublic void onReceivedIcon(WebView view, Bitmap icon) {//}@Overridepublic boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {//return true;}@Overridepublic void onCloseWindow(WebView window) {}// 处理 alert 弹出框,html 弹框的一种方式@Overridepublic boolean onJsAlert(WebView view, String url, String message, JsResult result) {//return true;}// 处理 confirm 弹出框@Overridepublic boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResultresult) {//return true;}// 处理 prompt 弹出框@Overridepublic boolean onJsConfirm(WebView view, String url, String message, JsResult result) {//return true;}};// 同样,将上面定义的 WebChromeClient 设置给 WebView :webView.setWebChromeClient(mWebChromeClient);
WebChromeClient 调用 Android 相机和相册
private int CAMERA_CODE_REQUEST = 0;private ValueCallback<Uri> mUploadMessage;private ValueCallback<Uri[]> mUploadCallbackAboveL;public static final int FILECHOOSER_RESULTCODE = 5173;public static final int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 5174;/*** 申请拍照权限*/public static final int REQUEST_ACCESS_COARSE_CAMERA = 00002;/*** 拍照裁剪*/private Uri imageUri;mWebView.setWebChromeClient(new WebChromeClient() {@Overridepublic void onReceivedTitle(WebView view, String webtitle) {super.onReceivedTitle(view, webtitle);}@Overridepublic void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {callback.invoke(origin, true, false);super.onGeolocationPermissionsShowPrompt(origin, callback);}// For Android < 3.0public void openFileChooser(ValueCallback<Uri> uploadMsg) {this.openFileChooser(uploadMsg, "*/*");}// For Android >= 3.0public void openFileChooser(ValueCallback<Uri> uploadMsg,String acceptType) {Log.e("acceptType", acceptType);this.openFileChooser(uploadMsg, acceptType, null);}// For Android >= 4.1public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {Log.e("acceptType", acceptType + "capture" + capture);CAMERA_CODE_REQUEST = FILECHOOSER_RESULTCODE;mUploadMessage = uploadMsg;openCamera();}@Override@TargetApi(Build.VERSION_CODES.LOLLIPOP)public boolean onShowFileChooser(WebView mWebView,ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {CAMERA_CODE_REQUEST = FILECHOOSER_RESULTCODE_FOR_ANDROID_5;mUploadCallbackAboveL = filePathCallback;openCamera();return true;}});/*** 这里是权限处理,大家可以使用各自的方法进行处理*/public void openCamera() {//PermissionHelper.with(this).requestCode(REQUEST_ACCESS_COARSE_CAMERA).requestPermission(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE).request();}/*** 拍照权限申请成功BLUETOOTH*/@PermissionSucceed(requestCode = REQUEST_ACCESS_COARSE_CAMERA)public void onSuccess() {showCameraAction();}/*** 拍照权限申请失败*/@PermissionFail(requestCode = REQUEST_ACCESS_COARSE_CAMERA)private void onFail() {UIHelper.getSystemHintToast(this, "您拒绝了拍照权限,将不能正常拍照");}/*** 调用摄像头和相册添加图片*/protected void showCameraAction() {File file = FileUtils.getNewFile();imageUri = getUriForFile(this, file);final List<Intent> cameraIntents = new ArrayList<>();final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);final List<ResolveInfo> listCam = getPackageManager().queryIntentActivities(captureIntent, 0);for (ResolveInfo res : listCam) {final String packageName = res.activityInfo.packageName;final Intent i = new Intent(captureIntent);i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));i.setPackage(packageName);i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);cameraIntents.add(i);}Intent i = new Intent(Intent.ACTION_GET_CONTENT);i.addCategory(Intent.CATEGORY_OPENABLE);i.setType("image/*");Intent chooserIntent = Intent.createChooser(i, "图片上传");chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));startActivityForResult(chooserIntent, CAMERA_CODE_REQUEST);}protected Uri getUriForFile(Context context, File file) {if (context == null || file == null) {throw new NullPointerException();}Uri uri;if (Build.VERSION.SDK_INT >= 24) {uri = FileProvider.getUriForFile(context.getApplicationContext(), "com.realname.selfhelpdeclara.fileprovider", file);} else {uri = Uri.fromFile(file);}return uri;}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == FILECHOOSER_RESULTCODE) {if (null == mUploadMessage) {return;}Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();if (result != null) {mUploadMessage.onReceiveValue(result);} else {if (resultCode == RESULT_OK) {mUploadMessage.onReceiveValue(imageUri);} else {mUploadMessage.onReceiveValue(null);}}mUploadMessage = null;} else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {if (null == mUploadCallbackAboveL) {return;}Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();if (result != null) {mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});} else {if (resultCode == RESULT_OK) {Uri[] results = new Uri[]{imageUri};mUploadCallbackAboveL.onReceiveValue(results);} else {mUploadCallbackAboveL.onReceiveValue(null);}}mUploadCallbackAboveL = null;}}
5、调用 JS 代码
WebSettings webSettings = mWebView .getSettings();webSettings.setJavaScriptEnabled(true); // 这里必须设置mWebView.addJavascriptInterface(new InsertObj(), "jsObj");
以下方法是 Android 和 JS 的交互
public class InsertObj extends Object {private static Activity mActivity;private static WebView mWebView;public InsertObj(Activity activity, WebView webView) {mActivity = activity;mWebView = webView;}// 给 html 提供的方法,js 中可以通过:var str = window.jsObj.HtmlcallJava(); 获取到@JavascriptInterfacepublic String HtmlcallJava() {return "Html call Java";}// 给 html 提供的有参函数 : window.jsObj.HtmlcallJava2("IT-homer blog");@JavascriptInterfacepublic String HtmlcallJava2(final String result) {return "Html call Java : " + result;}// Html 给我们提供的函数@JavascriptInterfacepublic static void JavacallHtml() {mActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {// 这里是调用方法mWebView.loadUrl("javascript: showFromHtml()");Toast.makeText(mActivity, "clickBtn", Toast.LENGTH_SHORT).show();}});}// Html 给我们提供的有参函数@JavascriptInterfacepublic static void JavacallHtml2(final String param) {mActivity.runOnUiThread(new Runnable() {@Overridepublic void run() {mWebView.loadUrl("javascript: showFromHtml2('"+param+"')");Toast.makeText(mActivity, "clickBtn2", Toast.LENGTH_SHORT).show();}});}}
<!DOCTYPE html><html><head><meta content="text/html; charset=utf-8" http-equiv="Content-Type"><title>无标题 1</title><script type="text/javascript">function HtmlcallJava(){var str = window.jsObj.HtmlcallJava();alert(str);}function HtmlcallJava2(){var str = window.jsObj.HtmlcallJava2("HTML");alert(str);}function showFromHtml(){alert("我是js方法,我被Android后台调用");}function showFromHtml2(result){alert("我是js方法,我被Android后台调用 "+result);}</script></head><body><button onclick="HtmlcallJava()">HtmlcallJava</button><button onclick="HtmlcallJava2()">HtmlcallJava2</button><input type="file" accept="image/*" id="capture" capture="camera"></body></html>
8、设置 Cookie
public void setCookies(String cookie) {if (!TextUtils.isEmpty(cookie)) {String[] cookieArray = cookie.split(";");// 多个Cookie是使用分号分隔的for (int i = 0; i < cookieArray.length; i++) {int position = cookieArray[i].indexOf("=");// 在Cookie中键值使用等号分隔if (position != -1) {String cookieName = cookieArray[i].substring(0, position);// 获取键String cookieValue = cookieArray[i].substring(position + 1);// 获取值String value = cookieName + "=" + cookieValue;// 键值对拼接成 valueLog.i("cookie", value);CookieManager.getInstance().setCookie(getDomain(url), value);// 设置 Cookie}}}}/*** 获取URL的域名*/private String getDomain(String url) {url = url.replace("http://", "").replace("https://", "");if (url.contains("/")) {url = url.substring(0, url.indexOf('/'));}return url;}
7、WebView 返回键,返回上一页而不是退出浏览器
@Overridepublic boolean onKeyDown(int keyCode, KeyEvent event) {if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {mWebView.goBack();return true;}return super.onKeyDown(keyCode, event);}
8、销毁 WebView
@Overrideprotected void onDestroy(){if (mWebview != null) {mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);mWebview.clearHistory();((ViewGroup) mWebview.getParent()).removeView(mWebview);mWebview.destroy();mWebview = null;}return super.onDestroy();}
(完)