Webview上传图片(图片/拍照)

本文详细介绍了如何在WebView中实现图片上传功能,包括选择本地图片和使用相机拍照。通过对话框的方式展示选择选项,为用户提供便捷的交互体验。
之前没有接触过这一部分的,就是简单使用webview来加载界面,然后把要显示的东西展示出来,最近因为项目的需求,需要实现图片的上传,然后最后就要求也能拍照,参考了很多的博客,也试了很多,遇到了很多的坑,花了一些时间,不过最后终于解决了,还是很高兴,所以打算写一篇博客来记录一下,这也是我的第一篇博客,如果大家有好的想法还是可以给我提的。

  好了废话不多说,代码中聊:

webview_online = (CustomWebView) findViewById(R.id.webview_online);
        webview_online.setCookie(SharedpfUtils.getCookies(mContext));
        WebSettings webSettings = webview_online.getSettings();
        webSettings.setJavaScriptEnabled(true);
        webSettings.setBlockNetworkImage(false);
        webSettings.setDefaultTextEncodingName("utf-8");
        webSettings.setAllowFileAccess(true);
        webSettings.setUseWideViewPort(true);
        webSettings.setSupportMultipleWindows(true);
        webSettings.setLoadWithOverviewMode(true);
        webSettings.setBuiltInZoomControls(true);
        webSettings.setDisplayZoomControls(true);
        webSettings.setSupportZoom(true);
        webSettings.setAppCacheEnabled(true);
        webSettings.setJavaScriptCanOpenWindowsAutomatically(true);

这一部分应该不用解释的吧

webview_online.loadUrl(url);
//        webview_online.setWebViewClient(new WebViewClient());
        webview_online.setWebViewClient(new WebViewClient() {


            @Override
            public boolean shouldOverrideUrlLoading(WebView view, String url) {
                System.out.println("有网络正常加载");
                view.loadUrl(url);
                return super.shouldOverrideUrlLoading(view, url);
            }

            @Override
            public void onPageStarted(WebView view, String url, Bitmap favicon) {
                System.out.println("页面开始加载");


                super.onPageStarted(view, url, favicon);
            }

            @Override
            public void onPageFinished(WebView view, String url) {
                System.out.println("页面加载结束");

                super.onPageFinished(view, url);
            }

            @Override
            public void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {

                view.loadDataWithBaseURL(baseUrl, errorHtml, "text/html", "UTF-8", null);
                super.onReceivedError(view, request, error);

            }

            @Override
            public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
                view.loadDataWithBaseURL(baseUrl, errorHtml, "text/html", "UTF-8", null);
                super.onReceivedError(view, errorCode, description, failingUrl);
            }
        });

上面的这一部分应该也是不需要解释的:

 webview_online.setWebChromeClient(new WebChromeClient() {
            // For Android  > 4.1.1
            public void openFileChooser(ValueCallback<Uri> upLoading, String acceptType, String capture) {

                if (mUploadMessage != null) {
                    mUploadMessage.onReceiveValue(null);
                }
                mUploadMessage = upLoading;

             
               selectImage();
            }
            // For Android 3.0+
            public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
                if (mUploadMessage != null) {
                    mUploadMessage.onReceiveValue(null);
                }
                mUploadMessage = uploadMsg;
                selectImage();
            }

            // For Android < 3.0
            public void openFileChooser(ValueCallback<Uri> uploadMsg) {
               /* if (mUploadMessage != null) {
                    mUploadMessage.onReceiveValue(null);
                }
                mUploadMessage = uploadMsg;
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                intent.setType("");
                startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE);

                selectImage();*/

            }
            // For Android 5.0
            @TargetApi(Build.VERSION_CODES.LOLLIPOP)
            @Override
            public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {

                if (mUploadMessage != null) {
                    mUploadMessage.onReceiveValue(null);
                }
                Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
                intent.addCategory(Intent.CATEGORY_OPENABLE);
                if (fileChooserParams != null && fileChooserParams.getAcceptTypes() != null
                        && fileChooserParams.getAcceptTypes().length > 0) {
                    intent.setType(fileChooserParams.getAcceptTypes()[0]);
                } else {
                    intent.setType("");
                }
                startActivityForResult(Intent.createChooser(intent, "File Chooser"), FILECHOOSER_RESULTCODE);

                selectImage();
                return true;
            }

上面的这些代码,这些就需要说一下了,就是对你所选择的文件的一个回调,分别对应着android中的不同时期的版本,这个不是这次的重点,然后下面的这部分的代码是一个加载条,毕竟是为了美观,总不能加载的时候显示一个白白的页面吧;

 @Override
            public void onProgressChanged(WebView view, int newProgress) {
                if (newProgress == 100) {
                    closeDialog();
                } else {
                    openDialog(newProgress);
                }
            }

            private void openDialog(int newProgress) {
                if (progressDialog == null) {
                    progressDialog = new ProgressDialog(mContext);
                    progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
                    progressDialog.setTitle("正在加载中......");
                    progressDialog.show();
                } else {
                    progressDialog.setProgress(newProgress);
                }
            }

            private void closeDialog() {
                if (progressDialog != null && progressDialog.isShowing()) {

                    progressDialog.dismiss();
                    progressDialog = null;

                }
            }

        });

    }
下面才是关键的地方了,为了满足这个要求的话,还是需要有选择的,比如说是选择图片还是拍照还是取消;

 private void selectImage() {
        if (!FileUtils.checkSDcard(this)) {
            return;
        }
        String[] selectPicTypeStr = {"拍照", "图库","取消"};
        new AlertDialog.Builder(this)
                .setOnCancelListener(new ReOnCancelListener())
                .setItems(selectPicTypeStr,
                        new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                switch (which) {
                                    // 相机拍摄
                                    case 0:
                                        OpenCamera();
                                        break;
                                    // 手机相册
                                    case 1:
                                        ChosePic();
                                        break;
                                    case 2:
                                        if (mUploadMessagesAboveL != null) {
                                            mUploadMessagesAboveL.onReceiveValue(null);
                                            mUploadMessagesAboveL = null;
                                        } else if (mUploadMessage != null) {
                                            mUploadMessage.onReceiveValue(null);
                                            mUploadMessage = null;
                                        }

                                        break;
                                    default:
                                        break;
                                }
                            }
                        }).show();
    }
这边的这个取消还是很重要的,其实我考虑,其实不需要也是可以的,但是加上的还是更好一些 ,如果弹出对话框,用户选择一个图片或者进行拍照,但是进行到一半的时候,用户cancel了,这个时候再去点击“选择文件”按钮时,网页会失去响应。
 原因是:点击“选择文件”按钮时,网页会缓存一个ValueCallback对象,必须触发了该对象的onReceiveValue()方法,WebView才会释放,进而才能再一次的选择文件。
                        当弹层被取消时,返回未选择文件
                         调用系统app选择文件的时候,若弹出选择框,cancel掉选择框之后,发现webview无响应了,无法刷新,加载,点击,甚至退出这个activity也无法加载!后果很严重~
                        原因是当你选择上传文件的时候,webview的ValueCallback对象(就是选择图片的回调)会持有这个webview,在没有收到回调之前,你无法对这个webview做任何的操作!

上面的代码就是完成了一个dialog。

 //拍照
    private  void  OpenCamera() {
           if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
               File  SDpath   = Environment.getExternalStorageDirectory();

               System.out.println("SDpath+++" +SDpath);
               photoFile   = SDpath.getPath()+"/projects/photo/";
               File file   = new File(photoFile);

               if(!file.exists()){

                   file.mkdirs();
               }
               FileName = System.currentTimeMillis() + ".jpg";
               takephotoFile = new File(photoFile, FileName);
               mCameraUri = Uri.fromFile(takephotoFile);

               System.out.println("mCameraUri+++" + mCameraUri);
           }
                Intent  intent   = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.addCategory(Intent.CATEGORY_DEFAULT);
                intent.putExtra(MediaStore.EXTRA_OUTPUT,mCameraUri);
                startActivityForResult(intent,FILECHOOSE_TAKEPHOTOCODE);
    }

这个是拍照的部分的,这里面对刚拍的照片指定了一个路径,最后返回的是一个Uri,这个很重要,因为最后在这里遇到了一个大坑坑,说起大坑坑不要心跳哦,毕竟问题还是被解决了

  //选择照片
    private  void ChosePic() {

        Intent  intent  = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
        startActivityForResult(intent,FILECHOOSER_RESULTCODE );
    }

这个一部分是选择照片的,就是你可以设置是打开图库还是打开手机上有图片的文件夹,楼主之前只是选择上传图片的时候,就是把只要有图片的文件夹都给打开了,现在的这个话只是打开了部分的。还有就是针对不同的手机,跟平板的话,打开的文件夹确实是真的不一样的。

 @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if(requestCode == FILECHOOSER_RESULTCODE){
            if (null  == mUploadMessage)
                return;
            if(resultCode == RESULT_OK){

                result = data == null || resultCode != RESULT_OK ? null : data.getData();
            }else if (resultCode == RESULT_CANCELED){

                mUploadMessage.onReceiveValue(null);
                mUploadMessage = null;
                return;
            }


        }else  if (requestCode  == FILECHOOSE_TAKEPHOTOCODE){
            if (null == mUploadMessage)
                return;
           if(resultCode == RESULT_OK){
               result   = mCameraUri;

               this.sendBroadcast(new Intent(
                       Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));
           }else if (resultCode == RESULT_CANCELED){

            mUploadMessage.onReceiveValue(null);
            mUploadMessage = null;
            return;
        }



        }

            String path = FileUtils.getPath(this, result);
            if (TextUtils.isEmpty(path)) {
                mUploadMessage.onReceiveValue(null);
                mUploadMessage = null;
                return;
            }
            Uri uri = Uri.fromFile(new File(path));
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                mUploadMessage.onReceiveValue(new Uri[]{uri});
            } else {
                mUploadMessage.onReceiveValue(uri);
            }

            mUploadMessage = null;
        }

这个一部分可是重重之重的了,就是在这一部分消耗的时间最多的了,首先来说上面的那个Uri的问题,在这快的话,接收到是data,可是当你在运行的时候你发现拿不到这个值的,然后就试着打印了一下这个data的值,结果发现是null,麻蛋的这不是坑了吗,就说明上面的那个之没有传过来,然后解决的办法就是直接把上面的那个Uri直接传了过来,这样就解决了null的问题,把上传图片跟拍照设置不同的intent的flag,这样就分开了。中间还遇到了如果拍照的途中点击了返回的按钮或者在选照片的时候直接打开图库以后点击了关系等一些问题,上面都做了处理,不理解的地方可以留言,时间有限,就不聊那么多了,最后还有一段代码:

 /*
    * dialog监听类
    *
    * */

    private class ReOnCancelListener implements DialogInterface.OnCancelListener {
        @Override
        public void onCancel(DialogInterface dialogInterface) {
            if (mUploadMessage != null) {
                mUploadMessage.onReceiveValue(null);
                mUploadMessage = null;
            }

            if (mUploadMessagesAboveL != null) {
                mUploadMessagesAboveL.onReceiveValue(null);
                mUploadMessagesAboveL = null;
            }
        }
    }

第一次发博客,有不好地方或者有见解的地方,希望留言,谢谢!



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值