Android WebView 的使用

WebView 是 Android 中一个非常实用的组件,它内置了WebKit引擎,WebKit是一个开源的浏览器引擎,Chrome浏览器也是基于它,所以我们可以把WebView当做一个轻量级的浏览器使用。 WebView 可以使得网页轻松的内嵌到 app 里,还可以直接跟 js 相互调用。

1、添加网络权限

  1. <uses-permission android:name="android.permission.INTERNET" />

2、WebSettings 对访问页面进行设置。

  1. WebView mWebView = new WebView(this);
  2. WebSettings webSettings = mWebView .getSettings(); // 支持获取手势焦点,输入用户名、密码或其他
  3. mWebView.requestFocusFromTouch();
  4. webSettings.setJavaScriptEnabled(true); // 支持js
  5. webSettings.setUseWideViewPort(true); // 将图片调整到适合 webview 的大小
  6. webSettings.setLoadWithOverviewMode(true); // 缩放至屏幕的大小
  7. webSettings.setSupportZoom(true); // 支持缩放,默认为 true。是下面那个的前提。
  8. webSettings.setBuiltInZoomControls(true); // 设置内置的缩放控件。
  9. webSettings.setDisplayZoomControls(false); // 隐藏原生的缩放控件
  10. webSettings.setLayoutAlgorithm(LayoutAlgorithm.SINGLE_COLUMN); // 支持内容重新布局
  11. webSettings.supportMultipleWindows(); // 多窗口
  12. webSettings.setAllowFileAccess(true); // 设置可以访问文件
  13. webSettings.setNeedInitialFocus(true); // 当 webview 调用 requestFocus 时为 webview 设置节点
  14. webSettings.setJavaScriptCanOpenWindowsAutomatically(true); // 支持通过 JS 打开新窗口
  15. webSettings.setLoadsImagesAutomatically(true); // 支持自动加载图片
  16. webSettings.setDefaultTextEncodingName("utf-8"); // 设置编码格式
  17. webSettings.setDatabaseEnabled(true); // 设置支持本地存储
  18. String path = getActivity().getApplicationContext().getDir("cache", Context.MODE_PRIVATE).getPath(); // 取得缓存路径
  19. webSettings.setDatabasePath(path); // 设置缓存路径
  20. webSettings.setDomStorageEnabled(true); // 设置支持DomStorage
  21. webSettings.setCacheMode(WebSettings.LOAD_DEFAULT); // 设置存储模式
  22. //webSettings。setCacheMode(WebSettings.LOAD_CACHE_ELSE_NETWORK) ;//关闭webview中缓存
  23. webSettings.setAppCacheEnabled(true); // 设置支持缓存
  24. mWebView.requestFocus();

3、页面加载方式

  1. // 加载一个网页:
  2. mWebView.loadUrl("https://www.baidu.com/");
  3. // 加载 apk assets目录文件中的一个 html 页面
  4. mWebView.loadUrl("file:///android_asset/test.html");
  5. // 加载手机本地的一个 html 页面的方法:
  6. mWebView.loadUrl("content://com.android.htmlfileprovider/sdcard/test.html");

4、WebView 的两个重要方法 WebViewClient 和 WebChromeClient

WebViewClient 就是帮助 WebView 处理各种通知、请求事件的。

  1. // 打开网页时不调用系统浏览器,而是在本 WebView 中显示:
  2. mWebView.setWebViewClient(new WebViewClient(){
  3. @Override
  4. public boolean shouldOverrideUrlLoading(WebView view, String url) {
  5. view.loadUrl(url);
  6. return true;
  7. }
  8. });
  9. // 将上面定义的 webviewclinet 设置给 webview
  10. mWebView.setWebViewClient(webViewClient);

下面介绍 WebView 的一些事件:

  1. WebViewClient mWebViewClient = new WebViewClient()
  2. {
  3. //在网页上的所有加载都经过这个方法,这个函数我们可以做很多操作。
  4. //比如获取url,查看url.contains(“add”),进行添加操作
  5. shouldOverrideUrlLoading(WebView view, String url);
  6. //重写此方法才能够处理在浏览器中的按键事件。
  7. shouldOverrideKeyEvent(WebView view, KeyEvent event);
  8. //这个事件就是开始载入页面调用的,我们可以设定一个loading的页面,告诉用户程序在等待网络响应。
  9. onPageStarted(WebView view, String url, Bitmap favicon) ;
  10. //在页面加载结束时调用。同样道理,我们可以关闭loading 条,切换程序动作。
  11. onPageFinished(WebView view, String url);
  12. // 在加载页面资源时会调用,每一个资源(比如图片)的加载都会调用一次。
  13. onLoadResource(WebView view, String url) ;
  14. // (报告错误信息)
  15. onReceivedError(WebView view, int errorCode, String description, String failingUrl);
  16. //(更新历史记录)
  17. doUpdateVisitedHistory(WebView view, String url, boolean isReload);
  18. //(应用程序重新请求网页数据)
  19. onFormResubmission(WebView view, Message dontResend, Message resend);
  20. //(获取返回信息授权请求)
  21. onReceivedHttpAuthRequest(WebView view, HttpAuthHandler handler, String host,String realm);
  22. //重写此方法可以让webview处理https请求。
  23. onReceivedSslError(WebView view, SslErrorHandler handler, SslError error);
  24. // (WebView发生改变时调用)
  25. onScaleChanged(WebView view, float oldScale, float newScale);
  26. //(Key事件未被加载时调用)
  27. onUnhandledKeyEvent(WebView view, KeyEvent event);
  28. }

WebChromeClient 是辅助 WebView 处理 Javascript 的对话框,网站图标,网站 title,加载进度等。

  1. WebChromeClient mWebChromeClient = new WebChromeClient() {
  2. // 获得网页的加载进度,显示在右上角的 TextView 控件中
  3. @Override
  4. public void onProgressChanged(WebView view, int newProgress) {
  5. if (newProgress < 100) {
  6. String progress = newProgress + "%";
  7. } else {
  8. }
  9. }
  10. // 获取 Web 页中的 title 用来设置自己界面中的 title
  11. // 当加载出错的时候,比如无网络,这时 onReceiveTitle 中获取的标题为 找不到该网页,
  12. // 因此建议当触发 onReceiveError 时,不要使用获取到的 title
  13. @Override
  14. public void onReceivedTitle(WebView view, String title) {
  15. MainActivity.this.setTitle(title);
  16. }
  17. @Override
  18. public void onReceivedIcon(WebView view, Bitmap icon) {
  19. //
  20. }
  21. @Override
  22. public boolean onCreateWindow(WebView view, boolean isDialog, boolean isUserGesture, Message resultMsg) {
  23. //
  24. return true;
  25. }
  26. @Override
  27. public void onCloseWindow(WebView window) {
  28. }
  29. // 处理 alert 弹出框,html 弹框的一种方式
  30. @Override
  31. public boolean onJsAlert(WebView view, String url, String message, JsResult result) {
  32. //
  33. return true;
  34. }
  35. // 处理 confirm 弹出框
  36. @Override
  37. public boolean onJsPrompt(WebView view, String url, String message, String defaultValue, JsPromptResult
  38. result) {
  39. //
  40. return true;
  41. }
  42. // 处理 prompt 弹出框
  43. @Override
  44. public boolean onJsConfirm(WebView view, String url, String message, JsResult result) {
  45. //
  46. return true;
  47. }
  48. };
  49. // 同样,将上面定义的 WebChromeClient 设置给 WebView :
  50. webView.setWebChromeClient(mWebChromeClient);

WebChromeClient 调用 Android 相机和相册

  1. private int CAMERA_CODE_REQUEST = 0;
  2. private ValueCallback<Uri> mUploadMessage;
  3. private ValueCallback<Uri[]> mUploadCallbackAboveL;
  4. public static final int FILECHOOSER_RESULTCODE = 5173;
  5. public static final int FILECHOOSER_RESULTCODE_FOR_ANDROID_5 = 5174;
  6. /**
  7. * 申请拍照权限
  8. */
  9. public static final int REQUEST_ACCESS_COARSE_CAMERA = 00002;
  10. /**
  11. * 拍照裁剪
  12. */
  13. private Uri imageUri;
  14. mWebView.setWebChromeClient(new WebChromeClient() {
  15. @Override
  16. public void onReceivedTitle(WebView view, String webtitle) {
  17. super.onReceivedTitle(view, webtitle);
  18. }
  19. @Override
  20. public void onGeolocationPermissionsShowPrompt(String origin, GeolocationPermissions.Callback callback) {
  21. callback.invoke(origin, true, false);
  22. super.onGeolocationPermissionsShowPrompt(origin, callback);
  23. }
  24. // For Android < 3.0
  25. public void openFileChooser(ValueCallback<Uri> uploadMsg) {
  26. this.openFileChooser(uploadMsg, "*/*");
  27. }
  28. // For Android >= 3.0
  29. public void openFileChooser(ValueCallback<Uri> uploadMsg,String acceptType) {
  30. Log.e("acceptType", acceptType);
  31. this.openFileChooser(uploadMsg, acceptType, null);
  32. }
  33. // For Android >= 4.1
  34. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
  35. Log.e("acceptType", acceptType + "capture" + capture);
  36. CAMERA_CODE_REQUEST = FILECHOOSER_RESULTCODE;
  37. mUploadMessage = uploadMsg;
  38. openCamera();
  39. }
  40. @Override
  41. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  42. public boolean onShowFileChooser(WebView mWebView,ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
  43. CAMERA_CODE_REQUEST = FILECHOOSER_RESULTCODE_FOR_ANDROID_5;
  44. mUploadCallbackAboveL = filePathCallback;
  45. openCamera();
  46. return true;
  47. }
  48. });
  49. /**
  50. * 这里是权限处理,大家可以使用各自的方法进行处理
  51. */
  52. public void openCamera() {
  53. //
  54. PermissionHelper.with(this)
  55. .requestCode(REQUEST_ACCESS_COARSE_CAMERA)
  56. .requestPermission(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)
  57. .request();
  58. }
  59. /**
  60. * 拍照权限申请成功BLUETOOTH
  61. */
  62. @PermissionSucceed(requestCode = REQUEST_ACCESS_COARSE_CAMERA)
  63. public void onSuccess() {
  64. showCameraAction();
  65. }
  66. /**
  67. * 拍照权限申请失败
  68. */
  69. @PermissionFail(requestCode = REQUEST_ACCESS_COARSE_CAMERA)
  70. private void onFail() {
  71. UIHelper.getSystemHintToast(this, "您拒绝了拍照权限,将不能正常拍照");
  72. }
  73. /**
  74. * 调用摄像头和相册添加图片
  75. */
  76. protected void showCameraAction() {
  77. File file = FileUtils.getNewFile();
  78. imageUri = getUriForFile(this, file);
  79. final List<Intent> cameraIntents = new ArrayList<>();
  80. final Intent captureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
  81. final List<ResolveInfo> listCam = getPackageManager().queryIntentActivities(captureIntent, 0);
  82. for (ResolveInfo res : listCam) {
  83. final String packageName = res.activityInfo.packageName;
  84. final Intent i = new Intent(captureIntent);
  85. i.setComponent(new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
  86. i.setPackage(packageName);
  87. i.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
  88. cameraIntents.add(i);
  89. }
  90. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  91. i.addCategory(Intent.CATEGORY_OPENABLE);
  92. i.setType("image/*");
  93. Intent chooserIntent = Intent.createChooser(i, "图片上传");
  94. chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, cameraIntents.toArray(new Parcelable[]{}));
  95. startActivityForResult(chooserIntent, CAMERA_CODE_REQUEST);
  96. }
  97. protected Uri getUriForFile(Context context, File file) {
  98. if (context == null || file == null) {
  99. throw new NullPointerException();
  100. }
  101. Uri uri;
  102. if (Build.VERSION.SDK_INT >= 24) {
  103. uri = FileProvider.getUriForFile(context.getApplicationContext(), "com.realname.selfhelpdeclara.fileprovider", file);
  104. } else {
  105. uri = Uri.fromFile(file);
  106. }
  107. return uri;
  108. }
  109. @Override
  110. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  111. super.onActivityResult(requestCode, resultCode, data);
  112. if (requestCode == FILECHOOSER_RESULTCODE) {
  113. if (null == mUploadMessage) {
  114. return;
  115. }
  116. Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
  117. if (result != null) {
  118. mUploadMessage.onReceiveValue(result);
  119. } else {
  120. if (resultCode == RESULT_OK) {
  121. mUploadMessage.onReceiveValue(imageUri);
  122. } else {
  123. mUploadMessage.onReceiveValue(null);
  124. }
  125. }
  126. mUploadMessage = null;
  127. } else if (requestCode == FILECHOOSER_RESULTCODE_FOR_ANDROID_5) {
  128. if (null == mUploadCallbackAboveL) {
  129. return;
  130. }
  131. Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
  132. if (result != null) {
  133. mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});
  134. } else {
  135. if (resultCode == RESULT_OK) {
  136. Uri[] results = new Uri[]{imageUri};
  137. mUploadCallbackAboveL.onReceiveValue(results);
  138. } else {
  139. mUploadCallbackAboveL.onReceiveValue(null);
  140. }
  141. }
  142. mUploadCallbackAboveL = null;
  143. }
  144. }

5、调用 JS 代码

  1. WebSettings webSettings = mWebView .getSettings();
  2. webSettings.setJavaScriptEnabled(true); // 这里必须设置
  3. mWebView.addJavascriptInterface(new InsertObj(), "jsObj");

以下方法是 Android 和 JS 的交互

  1. public class InsertObj extends Object {
  2. private static Activity mActivity;
  3. private static WebView mWebView;
  4. public InsertObj(Activity activity, WebView webView) {
  5. mActivity = activity;
  6. mWebView = webView;
  7. }
  8. // 给 html 提供的方法,js 中可以通过:var str = window.jsObj.HtmlcallJava(); 获取到
  9. @JavascriptInterface
  10. public String HtmlcallJava() {
  11. return "Html call Java";
  12. }
  13. // 给 html 提供的有参函数 : window.jsObj.HtmlcallJava2("IT-homer blog");
  14. @JavascriptInterface
  15. public String HtmlcallJava2(final String result) {
  16. return "Html call Java : " + result;
  17. }
  18. // Html 给我们提供的函数
  19. @JavascriptInterface
  20. public static void JavacallHtml() {
  21. mActivity.runOnUiThread(new Runnable() {
  22. @Override
  23. public void run() {
  24. // 这里是调用方法
  25. mWebView.loadUrl("javascript: showFromHtml()");
  26. Toast.makeText(mActivity, "clickBtn", Toast.LENGTH_SHORT).show();
  27. }
  28. });
  29. }
  30. // Html 给我们提供的有参函数
  31. @JavascriptInterface
  32. public static void JavacallHtml2(final String param) {
  33. mActivity.runOnUiThread(new Runnable() {
  34. @Override
  35. public void run() {
  36. mWebView.loadUrl("javascript: showFromHtml2('"+param+"')");
  37. Toast.makeText(mActivity, "clickBtn2", Toast.LENGTH_SHORT).show();
  38. }
  39. });
  40. }
  41. }
  1. <!DOCTYPE html>
  2. <html>
  3. <head>
  4. <meta content="text/html; charset=utf-8" http-equiv="Content-Type">
  5. <title>无标题 1</title>
  6. <script type="text/javascript">
  7. function HtmlcallJava(){
  8. var str = window.jsObj.HtmlcallJava();
  9. alert(str);
  10. }
  11. function HtmlcallJava2(){
  12. var str = window.jsObj.HtmlcallJava2("HTML");
  13. alert(str);
  14. }
  15. function showFromHtml()
  16. {
  17. alert("我是js方法,我被Android后台调用");
  18. }
  19. function showFromHtml2(result)
  20. {
  21. alert("我是js方法,我被Android后台调用 "+result);
  22. }
  23. </script>
  24. </head>
  25. <body>
  26. <button onclick="HtmlcallJava()">HtmlcallJava</button>
  27. <button onclick="HtmlcallJava2()">HtmlcallJava2</button>
  28. <input type="file" accept="image/*" id="capture" capture="camera">
  29. </body>
  30. </html>

8、设置 Cookie

  1. public void setCookies(String cookie) {
  2. if (!TextUtils.isEmpty(cookie)) {
  3. String[] cookieArray = cookie.split(";");// 多个Cookie是使用分号分隔的
  4. for (int i = 0; i < cookieArray.length; i++) {
  5. int position = cookieArray[i].indexOf("=");// 在Cookie中键值使用等号分隔
  6. if (position != -1) {
  7. String cookieName = cookieArray[i].substring(0, position);// 获取键
  8. String cookieValue = cookieArray[i].substring(position + 1);// 获取值
  9. String value = cookieName + "=" + cookieValue;// 键值对拼接成 value
  10. Log.i("cookie", value);
  11. CookieManager.getInstance().setCookie(getDomain(url), value);// 设置 Cookie
  12. }
  13. }
  14. }
  15. }
  16. /**
  17. * 获取URL的域名
  18. */
  19. private String getDomain(String url) {
  20. url = url.replace("http://", "").replace("https://", "");
  21. if (url.contains("/")) {
  22. url = url.substring(0, url.indexOf('/'));
  23. }
  24. return url;
  25. }

7、WebView 返回键,返回上一页而不是退出浏览器

  1. @Override
  2. public boolean onKeyDown(int keyCode, KeyEvent event) {
  3. if ((keyCode == KeyEvent.KEYCODE_BACK) && mWebView.canGoBack()) {
  4. mWebView.goBack();
  5. return true;
  6. }
  7. return super.onKeyDown(keyCode, event);
  8. }

8、销毁 WebView

  1. @Override
  2. protected void onDestroy(){
  3. if (mWebview != null) {
  4. mWebview.loadDataWithBaseURL(null, "", "text/html", "utf-8", null);
  5. mWebview.clearHistory();
  6. ((ViewGroup) mWebview.getParent()).removeView(mWebview);
  7. mWebview.destroy();
  8. mWebview = null;
  9. }
  10. return super.onDestroy();
  11. }

(完)