发布成H5后,uni.chooseImage方法在android WebView上无法使用

  • 引言
  • 解决方案
  • 特别注意

引言

经过三个星期的折腾,uni-app的编码阶段宣告结束,正式进入到测试阶段。由于app有个功能需要引入第三方的android原生SDK,经过前期的测试论证决定将第三方的android原生sdk功能写在android的壳子里,在android的壳子里引入webview,然后将uni-app发布成H5,然后在webview里面调用H5页面。多么完美的方案,但是理想很丰满,显示很骨感啊,刚进入测试就发现uni-app的选择图片的方法uni.chooseImage死活不起作用。于是在DCloud问答社区搜索解决方案,看到这么一个帖子编译成H5后,uni.chooseImage方法在android WebView上无法使用,一毛一样有木有?o(╥﹏╥)o

官方给出的方案是让Webview支出图片选择,既然有了解决方案那就开干吧

解决方案

首先根据官方的提示在度娘上搜了一通,有这么篇文章深坑之Webview,解决H5调用android相机拍照和录像,不得不说大佬还是真的多啊,按照大佬的提示加上自己的需求来改造一下自己的webview(这里只贴出跟本功能相关的代码)

 private Uri imageUri;private ValueCallback<Uri> mUploadMessage;private ValueCallback<Uri[]> mUploadCallbackAboveL;private final static int PHOTO_REQUEST = 100;private final static int VIDEO_REQUEST = 120;private boolean videoFlag = false;private WebChromeClient webChromeClient = new WebChromeClient() {public void onProgressChanged(WebView view, int newProgress) {try {onProgress(view, newProgress);} catch (Exception e) {e.printStackTrace();}}// For Android 3.0-public void openFileChooser(ValueCallback<Uri> uploadMsg) {Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg)");mUploadMessage = uploadMsg;if (videoFlag) {recordVideo();} else {takePhoto();}}// For Android 3.0+public void openFileChooser(ValueCallback uploadMsg, String acceptType) {Log.d(TAG, "openFileChoose( ValueCallback uploadMsg, String acceptType )");mUploadMessage = uploadMsg;if (videoFlag) {recordVideo();} else {takePhoto();}}//For Android 4.1public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {Log.d(TAG, "openFileChoose(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");mUploadMessage = uploadMsg;if (videoFlag) {recordVideo();} else {takePhoto();}}// 本人以为5.0以前的大可不必处理,现在新建的android项目minSDK都建议22(也就是android5.0)起步了,再说了目前的环境先5.0以前的系统还真不好找,除了老年机// For Android 5.0+public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {Log.d(TAG, "onShowFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)");mUploadCallbackAboveL = filePathCallback;if (videoFlag) {recordVideo();} else {takePhoto();}return true;}};/*** 拍照*/private void takePhoto() {new ActionSheetDialog(MainActivity.this).builder().setCancelable(false).setCanceledOnTouchOutside(false).addSheetItem("拍照", ActionSheetDialog.SheetItemColor.Blue,new ActionSheetDialog.OnSheetItemClickListener() {@Overridepublic void onClick(int which) {File fileUri = new File(Environment.getExternalStorageDirectory().getPath() + "/" + SystemClock.currentThreadTimeMillis() + ".jpg");imageUri = Uri.fromFile(fileUri);if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {imageUri = FileProvider.getUriForFile(MainActivity.this, getPackageName() + ".fileprovider", fileUri);//通过FileProvider创建一个content类型的Uri}PhotoUtil.takePicture(MainActivity.this, imageUri, PHOTO_REQUEST);}}).addSheetItem("从相册选择", ActionSheetDialog.SheetItemColor.Blue,new ActionSheetDialog.OnSheetItemClickListener() {@Overridepublic void onClick(int which) {PhotoUtil.openPic(MainActivity.this, PHOTO_REQUEST);}}).addSheetItem("取消", ActionSheetDialog.SheetItemColor.Red,new ActionSheetDialog.OnSheetItemClickListener() {@Overridepublic void onClick(int which) {if (mUploadMessage != null) {mUploadMessage.onReceiveValue(null);mUploadMessage = null;}if (mUploadCallbackAboveL != null) {mUploadCallbackAboveL.onReceiveValue(null);mUploadCallbackAboveL = null;}}}).show();}/*** 录像*/private void recordVideo() {Intent intent = new Intent(MediaStore.ACTION_VIDEO_CAPTURE);intent.putExtra(MediaStore.EXTRA_VIDEO_QUALITY, 1);//限制时长intent.putExtra(MediaStore.EXTRA_DURATION_LIMIT, 10);//开启摄像机startActivityForResult(intent, VIDEO_REQUEST);}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == PHOTO_REQUEST) {if (null == mUploadMessage && null == mUploadCallbackAboveL) return;Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();if (mUploadCallbackAboveL != null) {onActivityResultAboveL(requestCode, resultCode, data);} else if (mUploadMessage != null) {mUploadMessage.onReceiveValue(result);mUploadMessage = null;}} else if (requestCode == VIDEO_REQUEST) {if (null == mUploadMessage && null == mUploadCallbackAboveL) return;Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();if (mUploadCallbackAboveL != null) {if (resultCode == RESULT_OK) {mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});mUploadCallbackAboveL = null;} else {mUploadCallbackAboveL.onReceiveValue(new Uri[]{});mUploadCallbackAboveL = null;}} else if (mUploadMessage != null) {if (resultCode == RESULT_OK) {mUploadMessage.onReceiveValue(result);mUploadMessage = null;} else {mUploadMessage.onReceiveValue(Uri.EMPTY);mUploadMessage = null;}}}}@TargetApi(Build.VERSION_CODES.LOLLIPOP)private void onActivityResultAboveL(int requestCode, int resultCode, Intent data) {if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null) {return;}Uri[] results = null;if (resultCode == Activity.RESULT_OK) {showToast("正在上传,请稍后……");if (data == null) {results = new Uri[]{imageUri};} else {String dataString = data.getDataString();ClipData clipData = data.getClipData();if (clipData != null) {results = new Uri[clipData.getItemCount()];for (int i = 0; i < clipData.getItemCount(); i++) {ClipData.Item item = clipData.getItemAt(i);results[i] = item.getUri();}}if (dataString != null)results = new Uri[]{Uri.parse(dataString)};}} else {showToast("没有选择图片!");}mUploadCallbackAboveL.onReceiveValue(results);mUploadCallbackAboveL = null;}private void showToast(String message) {Toast toast = Toast.makeText(this, null, Toast.LENGTH_LONG);toast.setText(message);toast.show();}private WebViewClient webViewClient = new WebViewClient() {@Overridepublic boolean shouldOverrideUrlLoading(WebView view, String url) {// 我这里主要用到的是拍照和选择图片,没有录像的功能,这个功能本可以去掉的,但是我很懒if (!TextUtils.isEmpty(url)) {videoFlag = url.contains("video");}if (url.trim().startsWith("tel")) {Intent i = new Intent(Intent.ACTION_VIEW);i.setData(Uri.parse(url));startActivity(i);} else {String port = url.substring(url.lastIndexOf(":") + 1, url.lastIndexOf("/"));//尝试要拦截的视频通讯url格式(808端口):【http://xxxx:808/?roomName】if (port.equals("808")) {//特殊情况【若打开的链接是视频通讯地址格式则调用系统浏览器打开】Intent i = new Intent(Intent.ACTION_VIEW);i.setData(Uri.parse(url));startActivity(i);} else {//其它非特殊情况全部放行view.loadUrl(url);}}return true;}@Overridepublic void onPageStarted(WebView view, String url, Bitmap favicon) {try {onPageBegin(view, url, favicon);} catch (Exception e) {e.printStackTrace();}}@Overridepublic void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {super.onReceivedError(view, errorCode, description, failingUrl);isLoadSuccess = false;if (isLoadSuccess) {layoutEmpty.setVisibility(View.GONE);} else {layoutEmpty.setVisibility(View.VISIBLE);}}@Overridepublic void onReceivedError(WebView view, WebResourceRequest request, WebResourceError error) {super.onReceivedError(view, request, error);isLoadSuccess = false;if (isLoadSuccess) {layoutEmpty.setVisibility(View.GONE);} else {layoutEmpty.setVisibility(View.VISIBLE);}}@Overridepublic void onPageFinished(WebView view, String url) {try {onPageEnd(view, url);} catch (Exception e) {e.printStackTrace();}}};

至于代码中用到的PhotoUtil上面提到的大佬的博客中已经给出了,以防万一这里贴一下

import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;public class PhotoUtil {private static final String TAG = "PhotoUtil";/*** @param activity    当前activity* @param imageUri    拍照后照片存储路径* @param requestCode 调用系统相机请求码*/public static void takePicture(Activity activity, Uri imageUri, int requestCode) {//调用系统相机Intent intentCamera = new Intent();if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {intentCamera.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件}intentCamera.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//将拍照结果保存至photo_file的Uri中,不保留在相册中intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);if (activity!=null){activity.startActivityForResult(intentCamera, requestCode);}}/*** @param activity    当前activity* @param requestCode 打开相册的请求码*/public static void openPic(Activity activity, int requestCode) {Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);photoPickerIntent.setType("image/*");activity.startActivityForResult(photoPickerIntent, requestCode);}/*** @param activity    当前activity* @param orgUri      剪裁原图的Uri* @param desUri      剪裁后的图片的Uri* @param aspectX     X方向的比例* @param aspectY     Y方向的比例* @param width       剪裁图片的宽度* @param height      剪裁图片高度* @param requestCode 剪裁图片的请求码*/public static void cropImageUri(Activity activity, Uri orgUri, Uri desUri, int aspectX, int aspectY, int width, int height, int requestCode) {Intent intent = new Intent("com.android.camera.action.CROP");if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);}intent.setDataAndType(orgUri, "image/*");intent.putExtra("crop", "true");intent.putExtra("aspectX", aspectX);intent.putExtra("aspectY", aspectY);intent.putExtra("outputX", width);intent.putExtra("outputY", height);intent.putExtra("scale", true);//将剪切的图片保存到目标Uri中intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri);intent.putExtra("return-data", false);intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());intent.putExtra("noFaceDetection", true);activity.startActivityForResult(intent, requestCode);}/*** 读取uri所在的图片** @param uri      图片对应的Uri* @param mContext 上下文对象* @return 获取图像的Bitmap*/public static Bitmap getBitmapFromUri(Uri uri, Context mContext) {try {//            Bitmap bitmap = MediaStore.Images.Media.getBitmap(mContext.getContentResolver(), uri);Bitmap bitmapFormUri = getBitmapFormUri(mContext, uri);return bitmapFormUri;} catch (Exception e) {e.printStackTrace();return null;}}/*** 通过uri获取图片并进行压缩** @param uri*/public static Bitmap getBitmapFormUri(Context ac, Uri uri) throws FileNotFoundException, IOException {InputStream input = ac.getContentResolver().openInputStream(uri);BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();onlyBoundsOptions.inJustDecodeBounds = true;onlyBoundsOptions.inDither = true;//optionalonlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optionalBitmapFactory.decodeStream(input, null, onlyBoundsOptions);input.close();int originalWidth = onlyBoundsOptions.outWidth;int originalHeight = onlyBoundsOptions.outHeight;if ((originalWidth == -1) || (originalHeight == -1)){return null;}//图片分辨率以480x800为标准float hh = 800f;//这里设置高度为800ffloat ww = 480f;//这里设置宽度为480f//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可int be = 1;//be=1表示不缩放if (originalWidth > originalHeight && originalWidth > ww) {//如果宽度大的话根据宽度固定大小缩放be = (int) (originalWidth / ww);} else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的话根据宽度固定大小缩放be = (int) (originalHeight / hh);}if (be <= 0){be = 1;}//比例压缩BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();bitmapOptions.inSampleSize = be;//设置缩放比例bitmapOptions.inDither = true;//optionalbitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888;//optionalinput = ac.getContentResolver().openInputStream(uri);Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);input.close();return compressImage(bitmap);//再进行质量压缩}/*** 质量压缩方法** @param image* @return*/public static Bitmap compressImage(Bitmap image) {ByteArrayOutputStream baos = new ByteArrayOutputStream();image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中int options = 100;while (baos.toByteArray().length / 1024 > 100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩baos.reset();//重置baos即清空baos//第一个参数 :图片格式 ,第二个参数: 图片质量,100为最高,0为最差  ,第三个参数:保存压缩后的数据的流image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中options -= 10;//每次都减少10}ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片return bitmap;}/*** @param context 上下文对象* @param uri     当前相册照片的Uri* @return 解析后的Uri对应的String*/@SuppressLint("NewApi")public static String getPath(final Context context, final Uri uri) {final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;String pathHead = "file:///";// DocumentProviderif (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {// ExternalStorageProviderif (isExternalStorageDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String type = split[0];if ("primary".equalsIgnoreCase(type)) {return pathHead + Environment.getExternalStorageDirectory() + "/" + split[1];}}// DownloadsProviderelse if (isDownloadsDocument(uri)) {final String id = DocumentsContract.getDocumentId(uri);final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));return pathHead + getDataColumn(context, contentUri, null, null);}// MediaProviderelse if (isMediaDocument(uri)) {final String docId = DocumentsContract.getDocumentId(uri);final String[] split = docId.split(":");final String type = split[0];Uri contentUri = null;if ("image".equals(type)) {contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;} else if ("video".equals(type)) {contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;} else if ("audio".equals(type)) {contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;}final String selection = "_id=?";final String[] selectionArgs = new String[]{split[1]};return pathHead + getDataColumn(context, contentUri, selection, selectionArgs);}}// MediaStore (and general)else if ("content".equalsIgnoreCase(uri.getScheme())) {return pathHead + getDataColumn(context, uri, null, null);}// Fileelse if ("file".equalsIgnoreCase(uri.getScheme())) {return pathHead + uri.getPath();}return null;}/*** Get the value of the data column for this Uri. This is useful for* MediaStore Uris, and other file-based ContentProviders.** @param context       The context.* @param uri           The Uri to query.* @param selection     (Optional) Filter used in the query.* @param selectionArgs (Optional) Selection arguments used in the query.* @return The value of the _data column, which is typically a file path.*/private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {Cursor cursor = null;final String column = "_data";final String[] projection = {column};try {cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);if (cursor != null && cursor.moveToFirst()) {final int column_index = cursor.getColumnIndexOrThrow(column);return cursor.getString(column_index);}} finally {if (cursor != null){cursor.close();}}return null;}/*** @param uri The Uri to check.* @return Whether the Uri authority is ExternalStorageProvider.*/private static boolean isExternalStorageDocument(Uri uri) {return "com.android.externalstorage.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is DownloadsProvider.*/private static boolean isDownloadsDocument(Uri uri) {return "com.android.providers.downloads.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is MediaProvider.*/private static boolean isMediaDocument(Uri uri) {return "com.android.providers.media.documents".equals(uri.getAuthority());}
}

以上代码就可以实现我所需要的功能了,如下图

特别注意

上述代码有两个需要特别注意的地方,如下图

具体原因在上面大佬的博客中有提到,这里不再赘述,有兴趣的可以仔细看看大佬的博客中所提到的第二个坑

一个坑浪费了一天,真的应了那句一杯茶一包烟,一个bug改一天,(ಥ﹏ಥ)

uni-app.04.发布成H5后,uni.chooseImage方法在android WebView上无法使用相关推荐

  1. python程序打包成apk_利用Gradle+Python3自动打包Android APK上传到蒲公英。

    利用Gradle+Python3自动打包Android APK上传到蒲公英. 面对每次都要打包一个版本发布给测试,都要手动打包签名~然后登录上传文件.这些繁琐的事情.于是就想到一句很经典的话,(人生苦 ...

  2. 苹果app用发布证书打包后怎么装手机测试

    一.背景 iOS 的开发证书打包后可以通过爱思助手安装到手机上测试,但发布 (Production) 证书是不允许安装在手机上的.而在实际开发过程中,尽管通过开发证书测试过了,iOS生产包仍然可能会出 ...

  3. Unity 发布成WebGL后场景变暗的问题

    一.光照变暗的对比图 虽不能说伸手不见五指,但也是乌漆嘛黑的挺恐怖. 二.变暗的原因 Unity官方对WebGL的说明 Global Illumination Unity WebGL only sup ...

  4. Ubuntu篇——Ubuntu20.04备份成ISO镜像文件并安装到其他电脑上(完整步骤)

    一.安装systemback. (如已安装请跳过) 1.要在Ubuntu 18.04或Ubuntu 18.10系统上安装systemback,首先删除PPA: sudo add-apt-reposit ...

  5. uniapp发布为H5并部署运行

    参考:uni-app 发布成 H5 后怎么部署到Web服务器上 1. 配置基础路径:mainfest.json->h5->填写运行的基础路径 image.png 配置为./,代表可在域名下 ...

  6. uni app(H5)中软键盘弹出,固定定位绝对定位元素位置发生错乱

    一.问题如下 最近用uni app写项目,发现H5软键盘弹起,导致我固定定位的按钮被顶了上去,如图 二.解决方法 1.让其在输入框获取焦点时隐藏 百度了许多,有让输入框获取焦点时让按钮隐藏的方法,失去 ...

  7. uni app push 集成小米

    重新设置 厂商推送设置 后,需要重新制作自定义基座,包括添加和修改 厂商推送设置 用自定义的基座打包好测试包,安装到手机上. 先测试小米后台是否能推送成功 推送后,手机就能接受到消息了 再看java代 ...

  8. taro 小程序转h5之后报错_使用taro框架将手百小程序转成h5总结

    前言 历时一周,终于成功兼容了h5和小程序,在此使用的taro框架,遇到的问题在此记录一下. 一.环境判断 使用 Taro,我们可以只书写一套代码,再通过 Taro 的编译工具,将源代码分别编译出可以 ...

  9. ppt html5转换,PPT还能转H5?这大概是制作招聘H5最快的方法了...

    原标题:PPT还能转H5?这大概是制作招聘H5最快的方法了... 各种H5页面屡屡刷爆朋友圈 炫酷.时尚.贱萌.文艺- 非常吸引眼球 特别是招聘类H5 总能吸引志同道合的小伙伴 但是! 我不会做... ...

最新文章

  1. spss预测变量重要性不可用_C4.5/5.0的SPSS操作
  2. 数据中心停机事故的教训:关注基础设施
  3. T-SQL语句之创建、修改、删除数据库
  4. 用c语言编写小于n的所有素数,关于求N以内素数的一点小问题(N小于一亿)
  5. Android adb 命令大全
  6. 如何让Linux上的GPG error 无法验证的这个公钥 NO_PUBKEY D97A3AE911FXXXXX 出错信息消失?
  7. 使用yq工具合并两个yml文件
  8. [图形学] 实时体积云(Horizon: Zero Dawn)
  9. 《犯罪心理》第一至第六季 名人名言 全
  10. 用户流量红利消退的下半场,淘宝如何保持高速增长?
  11. 计算机与资源管理器有何区别,在win7中双击打开计算机和右击打开资源管理器有什么区别...
  12. 计算机u盘打不开怎么办,电脑*u盘打不开怎么办
  13. ONF推新版Atrium 获得OpenDaylight支持
  14. centos6 安装redis
  15. Oculus内下游戏报错,OVR40779122解决办法
  16. 夕拾算法初级篇:5)1020. 月饼(贪心)
  17. 残差(residual)
  18. Python爬虫之四:使用BeautifulSoup爬取微博热搜
  19. Oriented Response Networks 论文笔记
  20. 资料:《新概念英语》旧版(第四册)原文及全文翻译

热门文章

  1. vivado synth 8-439 modulenot found
  2. C#多线程之Thread,ThreadPool,Task,Parallel
  3. 如何将试卷扫描成电子版?几个步骤轻松拿捏
  4. JAVADay1 初始、环境搭建、标识符、常量等
  5. 小程序源码:王者荣耀改名神器
  6. React-Native 关于oppo手机闪退的问题
  7. angularjs 点击div外面,隐藏该div
  8. 【vmware】centos虚拟机网络配置
  9. 【无标题】酸性固化剂的危害
  10. 通过 html2canvas对video视频点击按钮进行截取视频图片,获取base64图片