引言
在我们使用webView的时免不了要和原生的交互,其中就会遇到WebView上传文件的逻辑:当我们在Web页面上点击选择文件的控件()时,会回调WebChromeClient下的openFileChooser()(5.0及以上系统回调onShowFileChooser())。这个时候我们在openFileChooser方法中通过Intent打开系统相册或者支持该Intent的第三方应用来选择图片。
解决方式.
1.首先我们要自定义一个WebChromeClient继承WebChromeClient并实现相关的方法
Android3.0以下的调用
public void openFileChooser(ValueCallback<Uri> uploadMsg)
Android3.0以上:
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType)
Android4.4以下:
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture)
Android5.0以上:
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams)
提示:这里有个漏洞,4.4.x的由于系统内核发生了改变,没法调用以上方法,现在仍然找不到解决办法,唯一的方法就是4.4直接使用手机浏览器打开,这个是可以的。
具体代码如下:
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> mUploadCallbackAboveL;
public class MyWebViewChromeClient extends WebChromeClient {
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
openFileChooserImpl(uploadMsg);
}
//3.0--
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooserImpl(uploadMsg);
}
//4.1
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFileChooserImpl(uploadMsg);
}
@Override
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
openFileChooserImplForAndroid5(filePathCallback);
return true;
}
}
private void openFileChooserImplForAndroid5(ValueCallback<Uri[]> uploadMsg) {
mUploadCallbackAboveL = uploadMsg;
dispatchTakePictureIntent();
}
//5.0以下的掉用
private void openFileChooserImpl(ValueCallback<Uri> uploadMsg) {
mUploadMessage = uploadMsg;
dispatchTakePictureIntent();
}
//拍照
private void dispatchTakePictureIntent() {
selectImgDialog();
}
private void takePhoto() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getActivity().getPackageManager()) != null) {
Uri imageUri = null;
try {
imageUri = Uri.fromFile(createImageFile());
} catch (IOException e) {
e.printStackTrace();
}
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(takePictureIntent, FILECHOOSER_RESULTCODE);
}
}
/** * 209. * 本地相册选择图片 * 210. */ private void chosePic() {
Intent innerIntent = new Intent(Intent.ACTION_GET_CONTENT);
String IMAGE_UNSPECIFIED = "image/*";
innerIntent.setType(IMAGE_UNSPECIFIED); // 查看类型
Intent wrapperIntent = Intent.createChooser(innerIntent, null);
startActivityForResult(wrapperIntent, REQ_CHOOSE);
}
String mCurrentPhotoPath = null;
String FileName = "forum";
//创建文件夹包装图片
private File createImageFile() throws IOException {
File storageDir = new File(Util.getAppPath(getActivity()) + FileName);
if (!storageDir.exists()) {
storageDir.mkdirs();
}
storageDir = new File(Util.getAppPath(getActivity()) + FileName + "/", System.currentTimeMillis() + ".jpg");
//保存当前图片路径
mCurrentPhotoPath = storageDir.getAbsolutePath();
return storageDir;
}
//onActivityResult回调
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == FILECHOOSER_RESULTCODE || requestCode == REQ_CHOOSE) {
if (null == mUploadMessage && null == mUploadCallbackAboveL) return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (mUploadCallbackAboveL != null) {
onActivityResultAboveL(requestCode, data);
} else if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(result);
mUploadMessage = null;
}
}
}
//5.0以上版本,由于api不一样,要单独处理
// @TargetApi(Build.VERSION_CODES.LOLLIPOP)
private void onActivityResultAboveL(int requestCode, Intent data) {
if (mUploadCallbackAboveL == null) {
return;
}
Uri result = null;
if (requestCode == FILECHOOSER_RESULTCODE) {
File file = new File(mCurrentPhotoPath);
Uri localUri = Uri.fromFile(file);
Intent localIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE, localUri);
getActivity().sendBroadcast(localIntent);
result = Uri.fromFile(file);
} else if (requestCode == REQ_CHOOSE) {
result = data.getData();
}
mUploadCallbackAboveL.onReceiveValue(new Uri[]{result});
mUploadCallbackAboveL = null;
return;
}
解决5.0以下机型还是无法弹出的问题.
注意:现openFileChooser()是系统API,如果release包是开启了混淆的,所以在打包的时候混淆了openFileChooser(),这就导致无法回调openFileChooser()了。因此我们需要在混淆文件里面进行keep
-keepclassmembers class * extends android.webkit.WebChromeClient{
public void openFileChooser(...);
}
接下来解决某些设备只弹出一次问题
在stackoverflow搜了半天也没有找到合适答案,于是再看了下onShowFileChooser的api,忽然发现了一句话To cancel the request, call filePathCallback.onReceiveValue(null) and return true.
恍然大悟,原来取消依然是需要回调onReceiveValue。因此在当前页面onResume时候加上如下代码filePathCallback.onReceiveValue(null);
问题就可以得到解决。
在onactivityresult函数里面进行判断就可以了.
if (requestCode == UCrop.REQUEST_CROP || requestCode == REQ_CHOOSE) {
if (data == null) {
if (mUploadCallbackAboveL != null) {
mUploadCallbackAboveL.onReceiveValue(null);
mUploadCallbackAboveL = null;
}
if (mUploadMessage != null) {
mUploadMessage.onReceiveValue(null);
mUploadMessage = null;
}
}
}