webview怎么实现web的选择图片功能,如何让h5通过webview调用系统相册和相机,并在图片传回h5的时已经将图片做了压缩处理?本篇就是解决这方面的问题。
这边h5 的上传文件只是限定于图片类型,不需要pdf、txt等其他类型,如果要写一个普通的不限定于图片的上传文件功能可以参考https://www.jianshu.com/p/b0f1fdbfd502。
二、Webview实现Webview要调用系统相册/相机,需要setWebChromeClient并重写WebChromeClient的方法。
mWebView.setWebChromeClient(new WebChromeClient(){// For Android < 3.0public void openFileChooser(ValueCallback valueCallback) {mUploadCallBack = valueCallback;showFileChooser();}// For Android >= 3.0public void openFileChooser(ValueCallback valueCallback, String acceptType) {mUploadCallBack = valueCallback;showFileChooser();}//For Android >= 4.1public void openFileChooser(ValueCallback valueCallback, String acceptType, String capture) {mUploadCallBack = valueCallback;showFileChooser();}// For Android >= 5.0@Overridepublic boolean onShowFileChooser(WebView webView, ValueCallback filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {mUploadCallBackAboveL = filePathCallback;showFileChooser();return true;}});/** * 打开选择图片/相机 */private void showFileChooser() {Intent intent1 = new Intent(Intent.ACTION_PICK, null);intent1.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");//Intent intent1 = new Intent(Intent.ACTION_GET_CONTENT);//intent1.addCategory(Intent.CATEGORY_OPENABLE);//intent1.setType("image/*");Intent intent2 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);mCameraFilePath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator +System.currentTimeMillis() + ".jpg";if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// android7.0注意uri的获取方式改变Uri photoOutputUri = FileProvider.getUriForFile(MainActivity.this,BuildConfig.APPLICATION_ID + ".fileProvider",new File(mCameraFilePath));intent2.putExtra(MediaStore.EXTRA_OUTPUT, photoOutputUri);} else {intent2.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mCameraFilePath)));}Intent chooser = new Intent(Intent.ACTION_CHOOSER);chooser.putExtra(Intent.EXTRA_TITLE, "File Chooser");chooser.putExtra(Intent.EXTRA_INTENT,intent1);chooser.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[]{intent2});startActivityForResult(chooser, REQUEST_CODE_FILE_CHOOSER);}然后,在onActivityResult里处理获取到的图片。
@Overrideprotected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == REQUEST_CODE_FILE_CHOOSER) {Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();// 压缩到多少宽度以内int maxW = 1000;// 压缩到多少大小以内,1024kbint maxSize = 1024;if (result == null) {// 看是否从相机返回File cameraFile = new File(mCameraFilePath);if (cameraFile.exists()) {result = Uri.fromFile(cameraFile);sendBroadcast(new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, result));}}if (result != null) {// 根据uri获取路径String path = FileUtils.getPath(this, result);if (!TextUtils.isEmpty(path)) {File f = new File(path);if (f.exists() && f.isFile()) {// 按大小和尺寸压缩图片Bitmap b = getCompressBitmap(path, maxW, maxW, maxSize);String basePath = Environment.getExternalStorageDirectory().getAbsolutePath();String compressPath = basePath + File.separator + "photos" + File.separator+ System.currentTimeMillis() + ".jpg";// 压缩完保存在文件里if (saveBitmapToFile(b, compressPath)) {Uri newUri = null;if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {newUri = FileProvider.getUriForFile(MainActivity.this,BuildConfig.APPLICATION_ID + ".fileProvider",new File(compressPath));} else {newUri = Uri.fromFile(new File(compressPath));}if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {if (mUploadCallBackAboveL != null) {if (newUri != null) {mUploadCallBackAboveL.onReceiveValue(new Uri[]{newUri});mUploadCallBackAboveL = null;return;}}} else if (mUploadCallBack != null) {if (newUri != null) {mUploadCallBack.onReceiveValue(newUri);mUploadCallBack = null;return;}}}}}}clearUploadMessage();return;}}如果没有图片返回给h5,记得要执行下面代码,避免h5下次点击选择图片时无法响应。
/** * webview没有选择图片也要传null,防止下次无法执行 */private void clearUploadMessage(){if (mUploadCallBackAboveL != null) {mUploadCallBackAboveL.onReceiveValue(null);mUploadCallBackAboveL = null;}if (mUploadCallBack != null) {mUploadCallBack.onReceiveValue(null);mUploadCallBack = null;}}相关的压缩和保存图片代码如下:
/** * 根据路径获取bitmap(压缩后) * * @param srcPath 图片路径 * @param width最大宽(压缩完可能会大于这个,这边只是作为大概限制,避免内存溢出) * @param height 最大高(压缩完可能会大于这个,这边只是作为大概限制,避免内存溢出) * @param size图片大小,单位kb * @return 返回压缩后的bitmap */public static Bitmap getCompressBitmap(String srcPath, float width, float height, int size) {BitmapFactory.Options newOpts = new BitmapFactory.Options();// 开始读入图片,此时把options.inJustDecodeBounds 设回true了newOpts.inJustDecodeBounds = true;BitmapFactory.decodeFile(srcPath, newOpts);newOpts.inJustDecodeBounds = false;int w = newOpts.outWidth;int h = newOpts.outHeight;int scaleW = (int) (w / width);int scaleH = (int) (h / height);int scale = scaleW < scaleH ? scaleH : scaleW;if (scale size) {// 重置baos即清空baosbaos.reset();// 每次都减少10options -= 10;// 这里压缩options%,把压缩后的数据存放到baos中image.compress(Bitmap.CompressFormat.JPEG, options, baos);}// 把压缩后的数据baos存放到ByteArrayInputStream中ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把ByteArrayInputStream数据生成图片Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);return bitmap;}/** * bitmap保存为文件 * * @param bmbitmap * @param filePath 文件路径 * @return 返回保存结果 true:成功,false:失败 */public static boolean saveBitmapToFile(Bitmap bm, String filePath) {try {File file = new File(filePath);file.deleteOnExit();if (!file.getParentFile().exists()) {file.getParentFile().mkdirs();}BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream(file));boolean b = false;if (filePath.toLowerCase().endsWith(".png")) {b = bm.compress(Bitmap.CompressFormat.PNG, 100, bos);} else {b = bm.compress(Bitmap.CompressFormat.JPEG, 100, bos);}bos.flush();bos.close();return b;} catch (FileNotFoundException e) {} catch (IOException e) {}return false;}对于根据uri获取图片路径的代码也比较关键。相关代码可以参考https://www.jianshu.com/p/25c35da68db2
如果你的应用混淆了要注意下,openFileChooser方法并不是WebChromeClient的对外开放的方法,因此这个方法会被混淆,解决办法也比较简单,只需要在混淆文件里控制一下即可:
-keepclassmembers class * extends android.webkit.WebChromeClient{public void openFileChooser(...);}好了,这样就可以让webview调用原生相机和相册选择图片,并对图片做压缩处理。