最近公司项目碰到WebView上传图片的问题,在这方面纠结了挺长时间,把解决问题的思路记录一下。
至于 WebView 需要的配置方面这里就不说了,自行百度。
首先 WebView 要重写 WebViewClient 的 shouldOverrideUrlLoading 方法,让链接直接
在APP内部打开而不是跳转到系统浏览器或者是第三方浏览器:
webview.setWebViewClient(new WebViewClient(){
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
return super.shouldOverrideUrlLoading(view, url);
}
});
上面这个还不是遇到的问题,真正的问题就在于在 WebView 的链接里通过按钮打开手机相册,
但是安卓这么多版本又不会自动兼容,这就需要我们自己去做兼容。上网了解到在安卓5.0版本
以后SDK中开放的方法是 onShowFileChooser ,而在较低版本中是 openFileChooser 。然后在
API21(由于项目引用23的SDK,此方法被隐藏,不太清楚是不是21)之后 openFileCHooser 就
被 Google 隐藏掉了。
/**
* Tell the client to open a file chooser.
* @param uploadFile A ValueCallback to set the URI of the file to upload.
* onReceiveValue must be called to wake up the thread.a
* @param acceptType The value of the 'accept' attribute of the input tag
* associated with this file picker.
* @param capture The value of the 'capture' attribute of the input tag
* associated with this file picker.
*
* @deprecated Use {@link #showFileChooser} instead.
* @hide This method was not published in any SDK version.
*/
@SystemApi
@Deprecated
public void openFileChooser(ValueCallback<Uri> uploadFile, String acceptType, String capture) {
uploadFile.onReceiveValue(null);
}
从上面代码可以看出此方法被隐藏了,所以正常情况下这个方法是无法调用的。
但是如果非要用被隐藏的方法,那被隐藏的方法怎么才能引用呢?
第一种方法 可以利用反射机制。《这种方法自行百度搜索》
下面说说我在项目中用到的方法:
因为 openFileChooser 方法被隐藏 ,所以在 WebChromeClient 中重写的时候就没有此方法。
于是我便自己写一个类去继承 WebChromeClient 。在类中写兼容各个版本的 openFileChooser
方法。代码如下:
private ValueCallback<Uri> mUploadMessage;
private ValueCallback<Uri[]> uploadMessage; // 用于5.0以上
private static final int FILECHOOSER_RESULTCODE = 1;
public static final int REQUEST_SELECT_FILE = 100;
public class MyWebChromeClient extends WebChromeClient {
/**
* {@inheritDoc}
* 安卓3.0-
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
openFileChooser(uploadMsg, "");
}
/**
* {@inheritDoc}
* 安卓3.0 - 4.0
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
mUploadMessage = uploadMsg;
Intent i = null;
if (Build.VERSION.SDK_INT < 19) {
i = new Intent(Intent.ACTION_GET_CONTENT);
i.setType("image/*");
} else {
i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
}
startActivityForResult(Intent.createChooser(i, "选择图片"), FILECHOOSER_RESULTCODE);
}
/**
* {@inheritDoc}
* 安卓 4.0 - 5.0
*/
protected void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
openFileChooser(uploadMsg, acceptType);
}
/**
* 安卓5.0+
*/
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public boolean onShowFileChooser(WebView mWebView, ValueCallback<Uri[]> filePathCallback, WebChromeClient.FileChooserParams fileChooserParams) {
if (uploadMessage != null) {
uploadMessage.onReceiveValue(null);
uploadMessage = null;
}
uploadMessage = filePathCallback;
Intent intent = fileChooserParams.createIntent();
try {
startActivityForResult(Intent.createChooser(intent, "选择图片"), REQUEST_SELECT_FILE);
} catch (ActivityNotFoundException e) {
uploadMessage = null;
Toast.makeText(getApplicationContext(), "文件上传失败", Toast.LENGTH_LONG).show();
return false;
}
return true;
}
}
然后需要重写 onActivityResult 方法接收返回的数据即可:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return;
Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
if (result != null) {
uploadMessage.onReceiveValue(new Uri[]{result});
} else {
uploadMessage.onReceiveValue(new Uri[]{});
}
uploadMessage = null; //每次置空是为了防止下次点击按钮选择图片无响应
}
} else if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
mUploadMessage.onReceiveValue(result); //注意此处
mUploadMessage = null;
} else
Toast.makeText(getApplicationContext(), "上传文件失败", Toast.LENGTH_LONG).show();
return;
}
到这里工作就完成了90%,什么?到这里还没完成?
是的,在部分机型上上传图片之后,但是在WebView中显示图片缩略图确实TXT文档类型。
这是为什么?我个人理解认为是5.0以下与5.0以上图片返回的Uri格式不一致造成的。
一时间就把我难住了,尝试了各种方法,包括安卓不同版本系统打开图库的方式都无效。
突然大神附体似的就想到了会不会和返回 Uri 的格式不同有关?选择的图片肯定都是有
自己的路径的,我可不可以获取到图片的路径呢?
同时又发现有一个 Uri.fromFile() 的方法,而又可以将图片的路径转换成File类型。于是
拿起就是干马上尝试。
于是有了以下代码:
// 根据图片的Uri得到图片的路径
private String getImagePath(Uri originalUri) {
String[] pro = {MediaStore.Images.Media.DATA};
Cursor cursor = getContentResolver().query(originalUri, pro, null, null, null);
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String path = cursor.getString(column_index);
return path;
}
然后在onActivityResult中调用,完整代码:
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
if (requestCode == REQUEST_SELECT_FILE) {
if (uploadMessage == null)
return;
Uri result = (data == null || resultCode != RESULT_OK) ? null : data.getData();
if (result != null) {
uploadMessage.onReceiveValue(new Uri[]{result});
} else {
uploadMessage.onReceiveValue(new Uri[]{});
}
uploadMessage = null;
}
} else if (requestCode == FILECHOOSER_RESULTCODE) {
if (null == mUploadMessage)
return;
Uri result = data == null || resultCode != RESULT_OK ? null : data.getData();
Uri uri = Uri.fromFile(new File(getImagePath(result)));
mUploadMessage.onReceiveValue(uri); // 转换后
mUploadMessage = null;
} else
Toast.makeText(getApplicationContext(), "文件上传失败", Toast.LENGTH_LONG).show();
return;
}
正如上面,因为mUploadMessage.onReceiveValue(uri)中需要接收一个Uri类型的参数
因此又需要将图片路径转换成Uri。这样就奇迹般的起效了。然后就能正常的显示图片。
**
以上纯属个人理解,如有错误欢迎指出,一起共同学习。
**