Android WebView 拍照和选择图片
Android WebView 嵌套网页中有js调取拍照和选择图片上传的功能,这个需要在我们的代码中去实现方法
首先基础WebView设置
WebSettings settings=mWebView.getSettings(); settings.setJavaScriptEnabled(true); //设置webview支持javascript// settings.setJavaScriptCanOpenWindowsAutomatically(true); settings.setLoadsImagesAutomatically(true); //支持自动加载图片 settings.setUseWideViewPort(true); //设置webview推荐使用的窗口,使html界面自适应屏幕 settings.setLoadWithOverviewMode(true); settings.setSaveFormData(true); //设置webview保存表单数据 settings.setSavePassword(true); //设置webview保存密码 int mDensity=DensityUtils.getDensityDpi(context); if (mDensity==120) { settings.setDefaultZoom(WebSettings.ZoomDensity.CLOSE); } else if (mDensity==160) { settings.setDefaultZoom(WebSettings.ZoomDensity.MEDIUM); } else if (mDensity==240) { settings.setDefaultZoom(WebSettings.ZoomDensity.FAR); }// settings.setDefaultZoom(WebSettings.ZoomDensity.MEDIUM); //设置中等像素密度,medium=160dpi settings.setSupportZoom(true); //支持缩放 settings.setSupportMultipleWindows(true); settings.setAppCacheEnabled(true); //设置APP可以缓存 settings.setDatabaseEnabled(true); settings.setDomStorageEnabled(true);//返回上个界面不刷新 允许本地缓存 // settings.setCacheMode(WebSettings.LOAD_DEFAULT);// 设置缓存LOAD_DEFAULT LOAD_CACHE_ONLY,LOAD_NO_CACHE settings.setAllowFileAccess(true);// 设置可以访问文件 settings.setLayoutAlgorithm(WebSettings.LayoutAlgorithm.SINGLE_COLUMN);//不支持放大缩小 settings.setDisplayZoomControls(false);//不支持放大缩小 // NORMAL:正常显示,没有渲染变化。 // SINGLE_COLUMN:把所有内容放到WebView组件等宽的一列中。 //这个是强制的,把网页都挤变形了 // NARROW_COLUMNS:可能的话,使所有列的宽度不超过屏幕宽度。 //好像是默认的 mWebView.setLongClickable(true); mWebView.setScrollbarFadingEnabled(true); mWebView.setScrollBarStyle(View.SCROLLBARS_INSIDE_OVERLAY); mWebView.setDrawingCacheEnabled(true)
js调取拍照和选择图片功能,兼容3.0以上
//5.0以下使用 private ValueCallback<Uri> uploadMessage;// 5.0及以上使用 private ValueCallback<Uri[]> uploadMessageAboveL; //覆盖WebView默认使用第三方或系统默认浏览器打开网页的行为,使网页用WebView打开 mWebView.setWebChromeClient(new WebChromeClient() { // For Android < 3.0 public void openFileChooser(ValueCallback<Uri> valueCallback) { uploadMessage=valueCallback; openImageChooserActivity(); } // For Android >=3.0 public void openFileChooser(ValueCallback valueCallback, String acceptType) { uploadMessage=valueCallback; openImageChooserActivity(); } //For Android >=4.1 public void openFileChooser(ValueCallback<Uri> valueCallback, String acceptType, String capture) { uploadMessage=valueCallback; openImageChooserActivity(); } // For Android >=5.0 @Override public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) { uploadMessageAboveL=filePathCallback; openImageChooserActivity(); return true; } });
这里需要弹出一个对话框让用户选择拍照和图库,这里用的是第三方MaterialDialog
这里有个需要注意的地方,就是如果没有调用onReceiveValue(null)的方法,下一次js将无法生效,我这里让用户不能按返回键和点击屏幕外消失,当点击取消按钮的时候调用onReceiveValue(null)方法
//图片 private final static int FILE_CHOOSER_RESULT_CODE=128;//拍照 private final static int FILE_CAMERA_RESULT_CODE=129;//拍照图片路径 private String cameraFielPath; private void openImageChooserActivity() { new MaterialDialog.Builder(this) .items(R.array.photo) .positiveText("取消") .onPositive(new MaterialDialog.SingleButtonCallback() { @Override public void onClick(@NonNull MaterialDialog dialog, @NonNull DialogAction which) { if (uploadMessageAboveL !=null) { uploadMessageAboveL.onReceiveValue(null); uploadMessageAboveL=null; } if (uploadMessage !=null) { uploadMessage.onReceiveValue(null); uploadMessage=null; } dialog.dismiss(); } }) .cancelable(false) .canceledOnTouchOutside(false) .itemsCallback(new MaterialDialog.ListCallback() { @Override public void onSelection(MaterialDialog dialog, View itemView, int position, CharSequence text) { if (position==0) { takeCamera(); } else if (position==1) { takePhoto(); } } }).show(); } //选择图片 private void takePhoto() { Intent i=new Intent(Intent.ACTION_GET_CONTENT); i.addCategory(Intent.CATEGORY_OPENABLE); i.setType("image/*"); startActivityForResult(Intent.createChooser(i, "Image Chooser"), FILE_CHOOSER_RESULT_CODE); }//拍照 private void takeCamera() { Intent intent=new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (CommonUtil.hasSdcard()) { //这里可能需要检查文件夹是否存在 //File file=new File(Environment.getExternalStorageDirectory() + "/APPNAME/"); //if (!file.exists()) { // file.mkdirs(); //} cameraFielPath=Environment.getExternalStorageDirectory() + "upload.jpg"; File outputImage=new File(cameraFielPath); intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputImage)); startActivityForResult(intent, FILE_CAMERA_RESULT_CODE); } }
最后一步在界面onActivityResult中判断进行操作
@Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); if (null==uploadMessage && null==uploadMessageAboveL) return; if (resultCode !=RESULT_OK) {//同上所说需要回调onReceiveValue方法防止下次无法响应js方法 if (uploadMessageAboveL !=null) { uploadMessageAboveL.onReceiveValue(null); uploadMessageAboveL=null; } if (uploadMessage !=null) { uploadMessage.onReceiveValue(null); uploadMessage=null; } return; } Uri result=null; if (requestCode==FILE_CAMERA_RESULT_CODE) { if (null !=data && null !=data.getData()) { result=data.getData(); } if (result==null && hasFile(cameraFielPath)) { result=Uri.fromFile(new File(cameraFielPath)); } if (uploadMessageAboveL !=null) { uploadMessageAboveL.onReceiveValue(new Uri[]{result}); uploadMessageAboveL=null; } else if (uploadMessage !=null) { uploadMessage.onReceiveValue(result); uploadMessage=null; } } else if (requestCode==FILE_CHOOSER_RESULT_CODE) { if (data !=null) { result=data.getData(); } if (uploadMessageAboveL !=null) { onActivityResultAboveL(data); } else if (uploadMessage !=null) { uploadMessage.onReceiveValue(result); uploadMessage=null; } } } /** * 判断文件是否存在 */ public static boolean hasFile(String path) { try { File f=new File(path); if (!f.exists()) { return false; } } catch (Exception e) { // TODO: handle exception return false; } return true; } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void onActivityResultAboveL(Intent intent) { Uri[] results=null; if (intent !=null) { String dataString=intent.getDataString(); ClipData clipData=intent.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)}; } uploadMessageAboveL.onReceiveValue(results); uploadMessageAboveL=null; }
完结 ,代码就这么多 经过测试可以完成图片的选择和拍照。做个记录方便下次使用
最近在做ionic开发的时候,需要实现webview调用原生图片选择的功能。我们在ionic代码里使用<input>标签选择文件,浏览器调试的时候,没有任何问题,结果放到Android手机里面,发现点击选择的时候,没有任何反应。后来参考网上的方法,能够实现图片选择,但是在Android上只能选择一张,ios上不存在这些问题(不得不吐槽一下,Android原生webview相比ios差太多了,很容易有奇怪的问题)。最后在查看了<input>的标签属性以后,对ionic代码和原生代码做了一些修改,最终实现了多张图片选择的功能(只针对5.0做了修改)。
首先Android webview在响应文件选择的时候,会触发WebChromeClient的onShowFileChooser()方法。
默认实现
这个方法有三个参数,第二个参数用于最后回传文件的uri给webview,第三个参数是文件选择时的一些条件,跟<input>标签里面的属性有关。Android的此方法默认是返回false,拦截了文件选择响应,所以我们需要重写此方法,在里面添加文件选择的实现,并返回true。在添加文件选择的实现之前,我们先简单了解一下FileChooserParams和<input>标签的一些关系,方便我们后续的处理。
FileChooserParams和<input>标签的相关对应关系
关系图
这个里面的multiple属性比较坑,我当时没有在<input>标签上添加这个属性,在ios上可以多次选择文件,虽然一次只能选择一张,但是在Android上只能选择一次,如果想再添加新的文件,必须把之前的删掉才能添加,且最多只能有一个文件。加上这个属性以后,在Android和ios上还是有差异的,ios有了这个属性之后,默认允许同时选择多张图片,但是在Android上还需要额外处理才能同时选择多张。
最后实现的代码片段如下:
ionic页面
原生实现
文件选择
处理完文件选择后,调用valueCallback的onReceiveValue方法,webview里的input标签才会调用change。以下是选择完文件后的回调处理,如果成功得到图片地址,则调用onReceiveValue方法把图片的本地uri数组回传给webview,失败或异常情况回传null给webview。
回调处理
valueCallback的回调执行以后,会触发<input>标签的change方法,在这个方法里面添加渲染图片的功能,这样就能完美实现图片选择和预览的功能了!
上一篇:BGD娃娃和SD娃娃的区别?
下一篇:oicq是什么软件_1
发表评论