轉(zhuǎn)載自CSDN https://blog.csdn.net/ycqfdqy/article/details/142612478 供學(xué)習(xí)使用,轉(zhuǎn)載請(qǐng)聯(lián)系原作者
背景:
博主的項(xiàng)目是用webview嵌套網(wǎng)頁(yè)的形式實(shí)現(xiàn)的,但是在開(kāi)發(fā)過(guò)程中遇到了這樣一個(gè)問(wèn)題,前端頁(yè)面使用vant-uploadder(同input type='file’標(biāo)簽)時(shí)無(wú)法調(diào)用攝像頭以及相冊(cè),但是在chrome等手機(jī)瀏覽器中又是可打開(kāi)的。
原因分析
根據(jù)前人描述,是因?yàn)?Android 源碼中將這部分屏蔽了,需要在 webView.setWebChromeClient(new WebChromeClient()) 中重寫 WebChromeClient 的 openFileChooser() 等方法。
解決方案:
不多說(shuō)直接貼代碼:
WebChromeClient 中重寫的方法有很多
/**
* 8(Android 2.2) <= API <= 10(Android 2.3)回調(diào)此方法
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg) {
}
/**
* 11(Android 3.0) <= API <= 15(Android 4.0.3)回調(diào)此方法
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType) {
}
/**
* 16(Android 4.1.2) <= API <= 20(Android 4.4W.2)回調(diào)此方法
*/
public void openFileChooser(ValueCallback<Uri> uploadMsg, String acceptType, String capture) {
}
/**
* API >= 21(Android 5.0.1)回調(diào)此方法
*/
@Override
public boolean onShowFileChooser(WebView webView, ValueCallback<Uri[]> filePathCallback, FileChooserParams fileChooserParams) {
}
現(xiàn)在哪還有安卓5.0的設(shè)備果斷使用最后一個(gè)依舊直接貼代碼
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;
//拍照?qǐng)D片存儲(chǔ)位置及名稱
//Android 10(API級(jí)別29)開(kāi)始,直接通過(guò)文件路徑訪問(wèn)存儲(chǔ)變得更加受限,推薦使用MediaStore或FileProvider來(lái)創(chuàng)建和管理文件URI。這里我通過(guò)MediaStore來(lái)實(shí)現(xiàn)
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);
//這里加兩個(gè)intent回在底部彈窗可以讓用戶選擇相冊(cè)上傳還是拍照上傳
Intent chooserIntent = Intent.createChooser(Photo, "請(qǐng)選擇");
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Parcelable[]{captureIntent});
//StartActivityForResult被廢棄后的用法
filePickerLauncher.launch(chooserIntent);
return true;
}
};
//這個(gè)用法每一個(gè)launch都要新建一個(gè) 不用requestcode來(lái)區(qū)分了
private final ActivityResultLauncher<Intent> filePickerLauncher = registerForActivityResult(new ActivityResultContracts.StartActivityForResult(), result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
// 更新系統(tǒng)圖庫(kù) 不知道有啥用 前人這么寫的
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) {//如果是用相冊(cè)上傳的那么這里是有值的直接返回
uris = new Uri[]{uriData};
uploadMessageAboveL.onReceiveValue(uris);
} else {
uploadMessageAboveL.onReceiveValue(new Uri[]{imageUri});//如果是用拍照上傳的那么這里是有值的直接將上面的imageUri直接返回
}
}
} else {
uploadMessageAboveL.onReceiveValue(null);
}
uploadMessageAboveL = null;
});
到這里基本大功告成了
坑:
注意:前面步驟完成后你會(huì)發(fā)現(xiàn)點(diǎn)擊相冊(cè)沒(méi)反應(yīng),這是沒(méi)有相機(jī)權(quán)限導(dǎo)致的,這里可以手動(dòng)給權(quán)限也可以動(dòng)態(tài)申請(qǐng)權(quán)限:存儲(chǔ)權(quán)限及相機(jī)權(quán)限
首先AndroidManifest先進(jìn)行聲明
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
動(dòng)態(tài)申請(qǐng):更坑的一點(diǎn)是安卓13還是14以上的存儲(chǔ)權(quán)限進(jìn)行了細(xì)分直接上代碼
private void permissionApplication() {
List<String> PERMISSIONS_STORAGE = new ArrayList<>();
//相機(jī)權(quán)限
PERMISSIONS_STORAGE.add(Manifest.permission.CAMERA);
//存儲(chǔ)權(quán)限
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狀態(tài)碼隨便設(shè)置如果要處理界面那必須和result方法中的requestCode一致相當(dāng)于key
requestPermissions(new String[]{permission}, PERMISSION_CODE);
}
}
}
//如果你想在權(quán)限申請(qǐng)后做些什么可以在下面方法中加
@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;
}
}
}