背景:
博主的项目是用webview嵌套网页的形式实现的,但是在开发过程中遇到了这样一个问题,前端页面使用vant-uploadder(同input type='file’标签)时无法调用摄像头以及相册,但是在chrome等手机浏览器中又是可打开的。
原因分析
根据前人描述,是因为 Android 源码中将这部分屏蔽了,需要在 webView.setWebChromeClient(new WebChromeClient()) 中重写 WebChromeClient 的 openFileChooser() 等方法。
解决方案:
不多说直接贴代码:
WebChromeClient 中重写的方法有很多
/**
* 8(Android 2.2) <= API <= 10(Android 2.3)回调此方法
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
}
/**
* 11(Android 3.0) <= API <= 15(Android 4.0.3)回调此方法
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
}
/**
* 16(Android 4.1.2) <= API <= 20(Android 4.4W.2)回调此方法
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
}
/**
* API >= 21(Android 5.0.1)回调此方法
*/
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
}
现在哪还有安卓5.0的设备果断使用最后一个依旧直接贴代码
public ValueCallback<Uri[]> uploadMessageAboveL;
private Uri imageUri;
private final WebChromeClient mWebChromeClient = new WebChromeClient() {
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
uploadMessageAboveL = filePathCallback;
//拍照图片存储位置及名称
//Android 10(API级别29)开始,直接通过文件路径访问存储变得更加受限,推荐使用MediaStore或FileProvider来创建和管理文件URI。这里我通过MediaStore来实现
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DISPLAY_NAME, "IMG_" + System.currentTimeMillis() + ".jpg");
values.put(MediaStore.Images.Media.MIME_TYPE, "image/jpeg");
values.put(MediaStore.Images.Media.RELATIVE_PATH, Environment.DIRECTORY_PICTURES + File.separator + "YourAppName");
imageUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
String filePath = Environment.getExternalStorageDirectory() + File.separator
+ Environment.DIRECTORY_PICTURES + File.separator;
String fileName = "IMG_" + DateFormat.format("yyyyMMdd_hhmmss", Calendar.getInstance(Locale.CHINA)) + ".jpg";
imageUri = Uri.fromFile(new File(filePath + fileName));
}
//拍照intent
Intent captureIntent = new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE);
captureIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
//图片选择intent
Intent Photo = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
//这里加两个intent回在底部弹窗可以让用户选择相册上传还是拍照上传
Intent chooserIntent = Intent.createChooser(Photo, "请选择");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});
//StartActivityForResult被废弃后的用法
filePickerLauncher.launch(chooserIntent);
return true;
}
};
//这个用法每一个launch都要新建一个 不用requestcode来区分了
private final ActivityResultLauncher<Intent> filePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// 更新系统图库 不知道有啥用 前人这么写的
Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
intent.setData(imageUri);
sendBroadcast(intent);
Intent data = result.getData();
if (data != null) {
Uri[] uris;
Uri uriData = data.getData();
if (uriData != null) {//如果是用相册上传的那么这里是有值的直接返回
uris = new Uri[]{uriData};
uploadMessageAboveL.onReceiveValue(uris);
} else {
uploadMessageAboveL.onReceiveValue(new Uri[]{imageUri});//如果是用拍照上传的那么这里是有值的直接将上面的imageUri直接返回
}
}
} else {
uploadMessageAboveL.onReceiveValue(null);
}
uploadMessageAboveL = null;
});
到这里基本大功告成了
坑:
注意:前面步骤完成后你会发现点击相册没反应,这是没有相机权限导致的,这里可以手动给权限也可以动态申请权限:存储权限及相机权限
首先AndroidManifest先进行声明
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
动态申请:更坑的一点是安卓13还是14以上的存储权限进行了细分直接上代码
private void permissionApplication() {
List<String> PERMISSIONS_STORAGE = new ArrayList<>();
//相机权限
PERMISSIONS_STORAGE.add(Manifest.permission.CAMERA);
//存储权限
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
PERMISSIONS_STORAGE.add(Manifest.permission.READ_MEDIA_IMAGES);
PERMISSIONS_STORAGE.add(Manifest.permission.READ_MEDIA_VIDEO);
PERMISSIONS_STORAGE.add(Manifest.permission.READ_MEDIA_AUDIO);
} else {
PERMISSIONS_STORAGE.add(Manifest.permission.READ_EXTERNAL_STORAGE);
}
for (String permission : PERMISSIONS_STORAGE) {
if (ActivityCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
//PERMISSION_CODE状态码随便设置如果要处理界面那必须和result方法中的requestCode一致相当于key
requestPermissions(new String[]{permission}, PERMISSION_CODE);
}
}
}
//如果你想在权限申请后做些什么可以在下面方法中加
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
for (int i : grantResults) {
if (grantResults[i] != PackageManager.PERMISSION_GRANTED) {
return;
}
}
}