解决的问题
当webview
与h5
交互时,遇到选择图片的情况,如果使用了系统相册,默认是选一张的,可是h5
的代码是类似这样附带multiple
多选属性:
IOS
和Android
系统浏览器都可以识别并默认能多选,偏偏Android webview
不争气
解决的方案1:手动让系统相册自带多选(需要android5.0+)
系统相册既然默认是单选的,那肯定可以多选。直接上代码:
/**
* android 5.0(含) 系统自带的图片选择
*
*/
private void openFileChooseProcess5(ValueCallback filePathCallback,WebChromeClient.FileChooserParams fileChooserParams) {
mValueCallback = filePathCallback;
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
if (fileChooserParams != null && fileChooserParams.getMode() == WebChromeClient.FileChooserParams.MODE_OPEN_MULTIPLE) {
//关键在这
intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
}
startActivityForResult(intent, 0);
}
/**
* 5.0以下
*/
private void openFileChooseProcess(ValueCallback uploadMsg) {
mUploadMsg = uploadMsg;
Intent i = new Intent(Intent.ACTION_GET_CONTENT);
i.addCategory(Intent.CATEGORY_OPENABLE);
i.setType("image/*");
startActivityForResult(Intent.createChooser(i, "Image Chooser"), 0);
}
注意这句intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
必须加,否则还是单选。
方法openFileChooseProcess5
须在WebChromeClient
的覆盖方法onShowFileChooser
中调用,
具体代码如下
// For Android 3.0+
public void openFileChooser(ValueCallback uploadMsg, String acceptType) {
openFileChooseProcess(uploadMsg);
}
// For Android < 3.0
public void openFileChooser(ValueCallback uploadMsgs) {
openFileChooseProcess(uploadMsg);
}
// For Android > 4.1.1
// @Override
public void openFileChooser(ValueCallback uploadMsg, String acceptType, String capture) {
openFileChooseProcess(uploadMsg);
}
// For Android >= 5.0
@Override
public boolean onShowFileChooser(WebView webView,
ValueCallback filePathCallback,
WebChromeClient.FileChooserParams fileChooserParams) {
openFileChooseProcess5(filePathCallback,fileChooserParams);
return true;
}
实现当前Activity
的onActivityResult
方法,获得选中的图片并传给h5
,代码如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_CANCELED) {
if (mUploadMsg != null) {
mUploadMsg.onReceiveValue(null);
mUploadMsg = null;
}
if (mValueCallback != null) {
mValueCallback.onReceiveValue(null);
mValueCallback = null;
}
}
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case 0:
if (null == mUploadMsg && null == mValueCallback) {
return;
}
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
if (mValueCallback != null) {
//处理相关数据
onActivityResultAboveL(data);
} else if (mUploadMsg != null) {
mUploadMsg.onReceiveValue(result);
}
break;
default:
break;
}
}
}
//选中图片并传给js
@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)};
}
}
mValueCallback.onReceiveValue(results);
mValueCallback = null;
}
注意onActivityResultAboveL
方法的作用,多选的情况需要用到intent.getClipData()
获取选中数据,在intent.putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true);
这行代码的第一个参数源码里可以看到注释上倒数第二行有标记说明{@link #getClipData()}
/**
* Extra used to indicate that an intent can allow the user to select and
* return multiple items. This is a boolean extra; the default is false. If
* true, an implementation is allowed to present the user with a UI where
* they can pick multiple items that are all returned to the caller. When
* this happens, they should be returned as the {@link #getClipData()} part
* of the result Intent.
*
* @see #ACTION_GET_CONTENT
* @see #ACTION_OPEN_DOCUMENT
*/
public static final String EXTRA_ALLOW_MULTIPLE =
"android.intent.extra.ALLOW_MULTIPLE";
解决的方案2:自己写一个图片选择器或者调用第三方图片选择器库,并处理选择回调
public void showOptions() {
//调用自己实现的图片选择器
ImageUtils.pictureMultiSelected(getActivity(), PictureConfig.CHOOSE_REQUEST, 9);
}
在上面的openFileChooser
和onShowFileChooser
中调用此方法。方法内部的实现这里不贴了,回调如下:
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_CANCELED) {
if (mUploadMsg != null) {
mUploadMsg.onReceiveValue(null);
mUploadMsg = null;
}
if (mValueCallback != null) {
mValueCallback.onReceiveValue(null);
mValueCallback = null;
}
}
if (resultCode == Activity.RESULT_OK) {
switch (requestCode) {
case PictureConfig.CHOOSE_REQUEST:
List selectList = PictureSelector.obtainMultipleResult(data);
try {
if (mUploadMsg != null) {
String path;
if (selectList.get(0).isCompressed()) {
LogUtils.e("getCompressPath) =" + selectList.get(0).getCompressPath());
path = selectList.get(0).getCompressPath();
} else {
path = selectList.get(0).getPath();
}
if (!CheckUtils.isAvailable(path) || !new File(path).exists()) {
LogUtils.e("sourcePath empty or not exists.");
break;
}
File file = new File(path);
Uri uri = Uri.fromFile(file);
mUploadMsg.onReceiveValue(uri);
mUploadMsg = null;
} else if (mValueCallback != null) {
Uri[] uris = new Uri[selectList.size()];
for (int i = 0; i < selectList.size(); i++) {
String path;
if (selectList.get(i).isCompressed()) {
LogUtils.e("getCompressPath) =" + selectList.get(i).getCompressPath());
path = selectList.get(i).getCompressPath();
} else {
path = selectList.get(i).getPath();
}
File file = new File(path);
uris[i] = Uri.fromFile(file);
}
if (uris.length > 0) {
mValueCallback.onReceiveValue(uris);
} else {
LogUtils.e("sourcePath empty or not exists.");
}
break;
}
} catch (Exception e) {
e.printStackTrace();
}
break;
default:
break;
}
}
}
总结和发散
两种方法个人觉得第一种更省事,代码量更小。
脱离webview
,其实很多地方我们也会用到多图上传的操作,这时候如果不需要自定义UI、选择数量限制等可以直接使用第一种方法比较省事