uni-app.04.发布成H5后,uni.chooseImage方法在android WebView上无法使用
发布成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上无法使用相关推荐
- python程序打包成apk_利用Gradle+Python3自动打包Android APK上传到蒲公英。
利用Gradle+Python3自动打包Android APK上传到蒲公英. 面对每次都要打包一个版本发布给测试,都要手动打包签名~然后登录上传文件.这些繁琐的事情.于是就想到一句很经典的话,(人生苦 ...
- 苹果app用发布证书打包后怎么装手机测试
一.背景 iOS 的开发证书打包后可以通过爱思助手安装到手机上测试,但发布 (Production) 证书是不允许安装在手机上的.而在实际开发过程中,尽管通过开发证书测试过了,iOS生产包仍然可能会出 ...
- Unity 发布成WebGL后场景变暗的问题
一.光照变暗的对比图 虽不能说伸手不见五指,但也是乌漆嘛黑的挺恐怖. 二.变暗的原因 Unity官方对WebGL的说明 Global Illumination Unity WebGL only sup ...
- Ubuntu篇——Ubuntu20.04备份成ISO镜像文件并安装到其他电脑上(完整步骤)
一.安装systemback. (如已安装请跳过) 1.要在Ubuntu 18.04或Ubuntu 18.10系统上安装systemback,首先删除PPA: sudo add-apt-reposit ...
- uniapp发布为H5并部署运行
参考:uni-app 发布成 H5 后怎么部署到Web服务器上 1. 配置基础路径:mainfest.json->h5->填写运行的基础路径 image.png 配置为./,代表可在域名下 ...
- uni app(H5)中软键盘弹出,固定定位绝对定位元素位置发生错乱
一.问题如下 最近用uni app写项目,发现H5软键盘弹起,导致我固定定位的按钮被顶了上去,如图 二.解决方法 1.让其在输入框获取焦点时隐藏 百度了许多,有让输入框获取焦点时让按钮隐藏的方法,失去 ...
- uni app push 集成小米
重新设置 厂商推送设置 后,需要重新制作自定义基座,包括添加和修改 厂商推送设置 用自定义的基座打包好测试包,安装到手机上. 先测试小米后台是否能推送成功 推送后,手机就能接受到消息了 再看java代 ...
- taro 小程序转h5之后报错_使用taro框架将手百小程序转成h5总结
前言 历时一周,终于成功兼容了h5和小程序,在此使用的taro框架,遇到的问题在此记录一下. 一.环境判断 使用 Taro,我们可以只书写一套代码,再通过 Taro 的编译工具,将源代码分别编译出可以 ...
- ppt html5转换,PPT还能转H5?这大概是制作招聘H5最快的方法了...
原标题:PPT还能转H5?这大概是制作招聘H5最快的方法了... 各种H5页面屡屡刷爆朋友圈 炫酷.时尚.贱萌.文艺- 非常吸引眼球 特别是招聘类H5 总能吸引志同道合的小伙伴 但是! 我不会做... ...
最新文章
- spss预测变量重要性不可用_C4.5/5.0的SPSS操作
- 数据中心停机事故的教训:关注基础设施
- T-SQL语句之创建、修改、删除数据库
- 用c语言编写小于n的所有素数,关于求N以内素数的一点小问题(N小于一亿)
- Android adb 命令大全
- 如何让Linux上的GPG error 无法验证的这个公钥 NO_PUBKEY D97A3AE911FXXXXX 出错信息消失?
- 使用yq工具合并两个yml文件
- [图形学] 实时体积云(Horizon: Zero Dawn)
- 《犯罪心理》第一至第六季 名人名言 全
- 用户流量红利消退的下半场,淘宝如何保持高速增长?
- 计算机与资源管理器有何区别,在win7中双击打开计算机和右击打开资源管理器有什么区别...
- 计算机u盘打不开怎么办,电脑*u盘打不开怎么办
- ONF推新版Atrium 获得OpenDaylight支持
- centos6 安装redis
- Oculus内下游戏报错,OVR40779122解决办法
- 夕拾算法初级篇:5)1020. 月饼(贪心)
- 残差(residual)
- Python爬虫之四:使用BeautifulSoup爬取微博热搜
- Oriented Response Networks 论文笔记
- 资料:《新概念英语》旧版(第四册)原文及全文翻译