当前位置:   article > 正文

Android在WebView中使用H5来调用本地相机相册的方法_webchromclient的openfilechooser()只调用了一次

webchromclient的openfilechooser()只调用了一次

最近在做一个项目需求是在Android原生的Activity中嵌套一个WebView来做混合开发,之前也做过这样的开发方式,一般都是

纯H5的逻辑使用,但是这次使用的是H5来调用本地的相机相册,下面坑来了:

1:首先Android原生和H5的交互的集成问题。

2:H5中调用本地的相机相册和视频拍摄是调用不起来的,需要原生自己调用。

3:WebView在使用过程中会出现一系列问题。

首先解决第一个问题:

原生和H5交互的问题中为了方便一般使用第三方库,GitHub上有开源库,其中选择了一个Star比较多的https://github.com/lzyzsd/JsBridge,但是我在集成后出现了一个问题是H5发消息给原生,原生能接收到,但是原生发消息给H5的时候H5死活接收不到,这个问题调试了很久都没有调试成功,最后选择放弃了,使用了另外一种集成方式,也是使用的这个开源库,但是是以lib的形式集成到本地的,把lib包集成到本地项目中(资源下载):

   1:在app下的build.gradle的dependencies闭包中添加如下依赖

    compile project(':library')

   2:如果你的包中有多个依赖还要在defaultConfig闭包中添加如下依赖,不然打包会报错:

  1. defaultConfig {
  2. ................
  3. multiDexEnabled true
  4. }

   3:在项目的最外层settings.gradle中加入

   include ':app', ':library'

   4:OK集成完毕

然后解决第二个问题:

在原生代码中实例化WebView后做如下配置:

  1. WebSettings webSettings = webView.getSettings();
  2. webSettings.setDomStorageEnabled(true);//设置适应Html5 //重点是这个设置
  3. webSettings.setUseWideViewPort(true);
  4. webSettings.setLoadWithOverviewMode(true);
  5. webSettings.setDefaultTextEncodingName("UTF-8");
  6. webSettings.setAllowContentAccess(true); // 是否可访问Content Provider的资源,默认值 true
  7. webSettings.setAllowFileAccess(true); // 是否可访问本地文件,默认值 true
  8. // 是否允许通过file url加载的Javascript读取本地文件,默认值 false
  9. webSettings.setAllowFileAccessFromFileURLs(false);
  10. // 是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false
  11. webSettings.setAllowUniversalAccessFromFileURLs(false);
  12. webView.loadUrl(url);
  13. webView.setWebViewClient(new BridgeWebViewClient(webView){
  14. @Override
  15. public void onPageFinished(WebView view, String url) {
  16. if (mode == SingleH5.IEO || mode == SingleH5.H5APPLY || mode == SingleH5.APPMARKET) {
  17. String title = view.getTitle();
  18. if (!TextUtils.isEmpty(title)) {
  19. tvName.setText(title);
  20. }
  21. }
  22. super.onPageFinished(view, url);
  23. }
  24. });
  25. // webView.setDefaultHandler(new DefaultHandler());
  26. webView.setWebChromeClient(new MyChromeWebClient() {
  27. @Override
  28. public void onProgressChanged(WebView view, int newProgress) {
  29. // TODO 自动生成的方法存根,其中pb是WebView上面的进度条
  30. if (newProgress == 100) {
  31. pb.setVisibility(View.GONE);//加载完网页进度条消失
  32. } else {
  33. pb.setVisibility(View.VISIBLE);//开始加载网页时显示进度条
  34. pb.setProgress(newProgress);//设置进度值
  35. }
  36. }
  37. @Override
  38. public void onReceivedTitle(WebView view, String title) {
  39. super.onReceivedTitle(view, title);
  40. }
  41. });

2:我们重新写的MyChromeWebClient为:

  1. //自定义 WebChromeClient 辅助WebView处理图片上传操作【<input type=file> 文件上传标签】
  2. public class MyChromeWebClient extends WebChromeClient {
  3. // For Android 3.0-
  4. public void openFileChooser(ValueCallback<Uri> uploadMsg) {
  5. Log.d("openFileChoose", "openFileChoose(ValueCallback<Uri> uploadMsg)");
  6. mUploadMessage = uploadMsg;
  7. takePhoto();
  8. }
  9. // For Android 3.0+
  10. public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
  11. Log.d("openFileChoose", "openFileChoose( ValueCallback uploadMsg, String acceptType )");
  12. mUploadMessage = uploadMsg;
  13. takePhoto();
  14. }
  15. //For Android 4.1
  16. public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
  17. Log.d("openFileChoose", "openFileChoose(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");
  18. mUploadMessage = uploadMsg;
  19. takePhoto();
  20. }
  21. // For Android 5.0+
  22. public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
  23. Log.d("openFileChoose", "onShowFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");
  24. mUploadCallbackAboveL = filePathCallback;
  25. checkSelfPermission();
  26. // takePhoto();
  27. return true;
  28. }
  29. }
  30. /**
  31. * 拍照或选择相册
  32. */
  33. private void takePhoto() {
  34. AlertDialog.Builder alertDialog = new AlertDialog.Builder(ItWaterTableActivity.this);
  35. alertDialog.setTitle("选择");
  36. alertDialog.setOnCancelListener(new ReOnCancelListener());
  37. alertDialog.setItems(new CharSequence[]{"相机", "相册"},
  38. new DialogInterface.OnClickListener() {
  39. @Override
  40. public void onClick(DialogInterface dialog, int which) {
  41. if (which == 0) {
  42. File fileUri = new File(Environment.getExternalStorageDirectory().getPath() + "/" + SystemClock.currentThreadTimeMillis() + ".jpg");
  43. imageUri = Uri.fromFile(fileUri);
  44. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
  45. imageUri = FileProvider.getUriForFile(ItWaterTableActivity.this, "com.ib.android.fileprovider", fileUri);//通过FileProvider创建一个content类型的Uri
  46. }else {
  47. imageUri = Uri.fromFile(fileUri);
  48. }
  49. Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
  50. intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri);
  51. startActivityForResult(intent,PHOTO_REQUEST);
  52. } else {
  53. Intent i = new Intent(Intent.ACTION_GET_CONTENT);
  54. i.addCategory(Intent.CATEGORY_OPENABLE);
  55. i.setType("image/*");
  56. ItWaterTableActivity.this.startActivityForResult(Intent.createChooser(i, "File Browser"), PHOTO_REQUEST);
  57. }
  58. }
  59. });
  60. alertDialog.show();
  61. }
  62. /**
  63. * 点击取消的回调
  64. */
  65. private class ReOnCancelListener implements
  66. DialogInterface.OnCancelListener {
  67. @Override
  68. public void onCancel(DialogInterface dialogInterface) {
  69. if (mUploadMessage != null) {
  70. mUploadMessage.onReceiveValue(null);
  71. mUploadMessage = null;
  72. }
  73. if (mUploadCallbackAboveL != null) {
  74. mUploadCallbackAboveL.onReceiveValue(null);
  75. mUploadCallbackAboveL = null;
  76. }
  77. }
  78. }

3:在onActivityResult中接收如下:

  1. @Override
  2. protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  3. super.onActivityResult(requestCode, resultCode, data);
  4. if (requestCode == PHOTO_REQUEST) {
  5. if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
  6. Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
  7. if (mUploadCallbackAboveL != null) {
  8. onActivityResultAboveL(requestCode, resultCode, data);
  9. } else if (mUploadMessage != null) {
  10. mUploadMessage.onReceiveValue(result);
  11. mUploadMessage = null;
  12. }
  13. }
  14. }
  1. @TargetApi(Build.VERSION_CODES.LOLLIPOP)
  2. private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {
  3. if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null) {
  4. return;
  5. }
  6. Uri[] results = null;
  7. if (resultCode == Activity.RESULT_OK) {
  8. if (data == null) {
  9. results = new Uri[]{imageUri};
  10. } else {
  11. String dataString = data.getDataString();
  12. ClipData clipData = data.getClipData();
  13. if (clipData != null) {
  14. results = new Uri[clipData.getItemCount()];
  15. for (int i = 0; i < clipData.getItemCount(); i++) {
  16. ClipData.Item item = clipData.getItemAt(i);
  17. results[i] = item.getUri();
  18. }
  19. }
  20. if (dataString != null)
  21. results = new Uri[]{Uri.parse(dataString)};
  22. }
  23. }
  24. mUploadCallbackAboveL.onReceiveValue(results);
  25. mUploadCallbackAboveL = null;
  26. }

OK

现在解决第三个问题:

  问题一:权限问题:

调用相机之后,文件回调不了,其实根本就是没有回调.这个时候你要去检查你的webview的Settings了关键代码,是否给足了权限;

       WebSettings settings = webView.getSettings();
        settings.setUseWideViewPort(true);
        settings.setLoadWithOverviewMode(true);
        settings.setDomStorageEnabled(true);
        settings.setDefaultTextEncodingName("UTF-8");
        settings.setAllowContentAccess(true); // 是否可访问Content Provider的资源,默认值 true
        settings.setAllowFileAccess(true);    // 是否可访问本地文件,默认值 true
        // 是否允许通过file url加载的Javascript读取本地文件,默认值 false
        settings.setAllowFileAccessFromFileURLs(false);
        // 是否允许通过file url加载的Javascript读取全部资源(包括文件,http,https),默认值 false
        settings.setAllowUniversalAccessFromFileURLs(false);
        //开启JavaScript支持
        settings.setJavaScriptEnabled(true);
        // 支持缩放
        settings.setSupportZoom(true);
其中settings.setDomStorageEnabled(true);这个方法当时我没加,崩了一次;
 

问题二:WebChromeClient的openFileChooser()只调用了一次
首先了解为什么这个方法只调用了一次,看这篇博客就可
Android开发深入理解WebChromeClient之onShowFileChooser或openFileChooser使用说明
看了他基本你就了解怎么回事了,

简单来说就是每次调用onShowFileChooser或openFileChooser都要设置一个回调;不管你是不是选择了照片,如果没有就回传一个null,在本文上述代码中已经做了回调监听处理,所以这个问题就简单的解决了。

问题三:怎么区分是要调用相机是需要拍照还是录视频
1,这个时候你就要看WebViewClient的 shouldOverrideUrlLoading()方法了,我是通过拦截url来判断url里面是拍照还是录制视频来加一个flag,然后这样你调用你的相机的时候就可以分别处理了
2,另外如果实在拿不到这个flag,可以通过html的input标签来accept类型判断
在哪里拿呢

 //  5.0+ acceptType
   public boolean onShowFileChooser(WebView webView,
    ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
            String[] acceptTypes = fileChooserParams.getAcceptTypes();
}

  // For Android 3.0+acceptType
    public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
    }

这样就可以拿到是accept进行调用了,在本文中我的操作是在原生中调用了一个弹窗,然后让用户选择是拍照还是选择图片。

问题四:android 7.0的FileProvider的坑
看洪阳大佬的这篇博客基本就了解了
Android 7.0 行为变更 通过FileProvider在应用间共享文件吧

 

 

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/105039?site
推荐阅读
相关标签
  

闽ICP备14008679号