Android 的WebView有一个坑,当接入的H5当中如果有上传图片或者其他文件的时候,点击添加按钮是没有任何反应的,但是IOS不会有这个问题
问题根因:H5访问本地文件的时候,使用的是标签<input type="file">,WebView处于安全性的考虑限制了这个操作,所以H5是无法直接访问到安卓的本地文件的。
解决办法:重写WebviewChromeClient 中的WebviewChromeClient openFileChooser() 和onShowFileChooser()方法响应<input type="file">,然后通过本地代码调起图库,拍照或者本地文件夹选择的功能,左后在最后在 onActivityResult 把选择的图片 URI 回传WebviewChromeClient
注意点:
1. 在Activity中加载H5
2.Fragment中加载H5
代码示例:
var uploadMessage: ValueCallback<Uri?>? = null
var uploadMessageAboveL: ValueCallback<Array<Uri?>?>? = null
webView.webChromeClient = object : WebChromeClient() {
// For Android < 3.0
fun openFileChooser(valueCallback: ValueCallback<Uri?>?) {
uploadMessage = valueCallback
// 自己实现的打卡图库和调起摄像头的方法
openImageChooserActivity()
}
// For Android >= 3.0
fun openFileChooser(valueCallback: ValueCallback<*>?, acceptType: String?) {
uploadMessage = valueCallback as ValueCallback<Uri?>?
// 自己实现的打卡图库和调起摄像头的方法
openImageChooserActivity()
}
//For Android >= 4.1
fun openFileChooser(
valueCallback: ValueCallback<Uri?>?,
acceptType: String?,
capture: String?
) {
uploadMessage = valueCallback
// 自己实现的打卡图库和调起摄像头的方法
openImageChooserActivity()
}
// For Android >= 5.0
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri?>?>,
fileChooserParams: FileChooserParams
): Boolean {
uploadMessageAboveL = filePathCallback
// 自己实现的打卡图库和调起摄像头的方法
openImageChooserActivity()
return true
}
}
在Activity中加载H5
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
when (requestCode) {
CHOOSE_CERTIFICATE_IMAGE_ALBUM_REQUEST_CODE -> { //相簿
if (null == uploadMessage && null == uploadMessageAboveL) return
val pictures: java.util.ArrayList<PictureInfo>? = data?.getParcelableArrayListExtra<PictureInfo>("pictures")
var uri : Uri? = null
pictures?.takeIf { pictures.size > 0 }?.let {
if(!pictures[0].path.isNullOrEmpty()){
uri = Uri.fromFile(File( pictures[0].path))
}
}
if (uploadMessageAboveL != null) {
if(uri == null){
uploadMessageAboveL?.onReceiveValue(null)
}else{
uploadMessageAboveL?.onReceiveValue(arrayOf(uri))
}
uploadMessageAboveL = null
} else if (uploadMessage != null) {
uploadMessage?.onReceiveValue(uri)
uploadMessage = null
}
}
SetImgUtil.CODE_CAMERA_REQUEST -> { //拍照
val file = SetImgUtil.getOutputMediaFile(this, mCameraCacheFileName)
val uri = Uri.fromFile(file)
if (null == uploadMessage && null == uploadMessageAboveL) return
if (uploadMessageAboveL != null) {
if(uri == null){
uploadMessageAboveL?.onReceiveValue(null)
}else{
uploadMessageAboveL?.onReceiveValue(arrayOf(uri))
}
uploadMessageAboveL = null
} else if (uploadMessage != null) {
uploadMessage?.onReceiveValue(uri)
uploadMessage = null
}
}
else->{
//这里uploadMessage跟uploadMessageAboveL在不同系统版本下分别持有了
//WebView对象,在用户取消文件选择器的情况下,需给onReceiveValue传null返回值
//否则WebView在未收到返回值的情况下,无法进行任何操作,文件选择器会失效
if (uploadMessage != null) {
uploadMessage?.onReceiveValue(null)
uploadMessage = null
} else if (uploadMessageAboveL != null) {
uploadMessageAboveL?.onReceiveValue(null)
uploadMessageAboveL = null
}
}
}
}else{
//这里uploadMessage跟uploadMessageAboveL在不同系统版本下分别持有了
//WebView对象,在用户取消文件选择器的情况下,需给onReceiveValue传null返回值
//否则WebView在未收到返回值的情况下,无法进行任何操作,文件选择器会失效
if (uploadMessage != null) {
uploadMessage?.onReceiveValue(null)
uploadMessage = null
} else if (uploadMessageAboveL != null) {
uploadMessageAboveL?.onReceiveValue(null)
uploadMessageAboveL = null
}
}
}
在Fragment中加载H5
在Fragment加载H5需要在Fragment所在的Activity中的onActivityResult中接收数据,接收到到的数据可以通过广播或者EventBus等方式传递到Fragment,数据解析和回传给H5的方式和上面的onActivityResult中处理方式一样