通過startActivityForResult 調用相機拍照, 并由onActivityResult回調傳回拍照的結果
目錄
1. 擷取縮略圖
2.儲存完整尺寸的照片
(1) 讀寫目錄的權限
(2) 建立圖檔檔案(檔案三要素: 名稱、格式、所在路徑)
(3) 使用FileProvider 産生Uri
(4) 調用startActivityForResult, 并将Uri提供給Camera 存儲圖檔(由MediaStore.EXTRA_OUTPUT 攜帶給Camera)
(5) 檢視存儲圖檔的路徑
(6) 注意事項
1. 擷取縮略圖
private void dispatchTakePictureIntent() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//resolveActivity 傳回可處理 Intent 的第一個 Activity 元件
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
}
}
Intent 的action為: MediaStore.ACTION_IMAGE_CAPTURE
則傳回的結果在intent 對象的Bundle 對象中, 再通過Bundle 對象擷取 資料data 即位圖
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
log.info("onActivityResult requestCode=" + requestCode + ", resultCode=" + resultCode);
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
mImageView.setImageBitmap(imageBitmap);
}
}
2.儲存完整尺寸的照片
需要儲存在 裝置的公共外部儲存設備,以供所有應用通路。
合适的共享照片目錄由具有
DIRECTORY_PICTURES
參數的
getExternalStoragePublicDirectory()
提供。
(1) 讀寫目錄的權限
需要對目錄有讀寫權限 :對該目錄執行讀寫操作
READ_EXTERNAL_STORAGE
和
WRITE_EXTERNAL_STORAGE
權限。(寫權限預設包含了讀的權限)
<manifest ...>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
...
</manifest>
并且也要給使用者存儲的權限,否則程式會崩潰。(參考上一篇的權限驗證:https://blog.csdn.net/whjk20/article/details/113746500)
(2) 建立圖檔檔案(檔案三要素: 名稱、格式、所在路徑)
根據日期-時間戳的新照片傳回一個獨一無二的檔案名
String currentPhotoPath;
private File createImageFile() throws IOException {
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
String imageFileName = "JPEG_" + timeStamp + "_";
File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
File image = File.createTempFile(
imageFileName, /* prefix */
".jpg", /* suffix */
storageDir /* directory */
);
//Save a file: path for use with ACTION_VIEW intents
currentPhotoPath = image.getAbsolutePath();
return image;
}
currentPhotoPath 為圖檔儲存的路徑,當成功傳回結果時,就可以取出圖檔(後續)
存儲檔案路徑 File storageDir = getExternalFilesDir(Environment.DIRECTORY_PICTURES);
這個對應表示存儲在目前APP的路徑下: /storage/emulated/0/Android/data/com.example.activitytest/files/Pictures/
(3) 使用FileProvider 産生Uri (程序間傳遞資料)
photoFile = createImageFile();
Uri photoURI = FileProvider.getUriForFile(this, "com.example.activitytest.fileprovider", photoFile);
其中"com.example.activitytest.fileprovider" 為使用到的 FileProvider 的辨別
需要在AndroidManifest 中聲明FileProvider
<application/>
<provider
android:authorities="com.example.activitytest.fileprovider"
android:name="androidx.core.content.FileProvider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/file_paths">
</meta-data>
</provider>
</application>
其中,res/xml/file_paths.xml
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<external-files-path
name="my_images"
path="." />
</paths>
(4) 調用startActivityForResult, 并将Uri提供給Camera 存儲圖檔(由MediaStore.EXTRA_OUTPUT 攜帶給Camera)
完整調用代碼:
private void dispatchTakePictureIntent2() {
Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//resolveActivity 傳回可處理 Intent 的第一個 Activity 元件
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
// Create the File where the photo should go
File photoFile = null;
try {
photoFile = createImageFile();
} catch (IOException e) {
// Error occurred while creating the File
e.printStackTrace();
}
// Continue only if the File was successfully created
if (photoFile != null) {
Uri photoURI = FileProvider.getUriForFile(this,
"com.example.activitytest.fileprovider",
photoFile);
takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI);
startActivityForResult(takePictureIntent, REQUEST_TAKE_PHOTO);
}
}
}
(5) 檢視存儲圖檔的路徑
可以通過Android studio 的Device File Explorer 檢視
/storage/emulated/0/Android/data/com.example.activitytest/files/Pictures/JPEG_20210209_113934_1896150229632986072.jpg
(6) 注意事項
參考官網例子,FileProvider 的中繼資料配置 external-files-path 為如下,
<external-files-path name="my_images" path="Android/data/com.example.package.name/files/Pictures" />
實際上會發生FC,例如報錯誤:
FATAL EXCEPTION: main
Process: com.example.marek.myapplication, PID: 6747
java.lang.IllegalArgumentException: Failed to find configured root that contains /storage/emulated/0/Android/data/com.example.marek.myapplication/files/Pictures/JPEG_20170228_175633_470124220.jpg
解決方法為: path="."
參考:https://stackoverflow.com/questions/42516126/fileprovider-illegalargumentexception-failed-to-find-configured-root
<?xml version="1.0" encoding="utf-8"?>
<paths>
<external-path
name="external"
path="." />
<external-files-path
name="external_files"
path="." />
<cache-path
name="cache"
path="." />
<external-cache-path
name="external_cache"
path="." />
<files-path
name="files"
path="." />
</paths>
參考官網: https://developer.android.com/training/camera/photobasics
3. 添加圖檔到相冊中(未成功,待續)
嘗試發廣播 (ACTION_MEDIA_SCANNER_SCAN_FILE) 和 MediaScannerConnection 主動發送都不行
@Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
log.info("onActivityResult requestCode=" + requestCode + ", resultCode=" + resultCode);
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
Bundle extras = data.getExtras();
Bitmap imageBitmap = (Bitmap) extras.get("data");
log.info("onActivityResult imageBitmap=" + imageBitmap);
mImageView.setImageBitmap(imageBitmap);
} else if (requestCode == REQUEST_TAKE_PHOTO && resultCode == RESULT_OK) {
galleryAddPic();
}
}
private MediaScannerConnectionClient mediaScannerConnectionClient =
new MediaScannerConnectionClient() {
@Override
public void onMediaScannerConnected() {
//Toast.makeText(mContext, "onMediaScannerConnected", Toast.LENGTH_LONG).show();
log.info("onMediaScannerConnected");
mediaScannerConnection.scanFile(currentPhotoPath, null);
}
@Override
public void onScanCompleted(String path, Uri uri) {
//Toast.makeText(mContext, "onScanCompleted path="+path + ", uri="+path, Toast.LENGTH_LONG).show();
log.info("onScanCompleted path="+path + ", uri="+path);
if (path.equals(currentPhotoPath))
mediaScannerConnection.disconnect();
}
};
MediaScannerConnection mediaScannerConnection = new MediaScannerConnection(this, mediaScannerConnectionClient);
private void galleryAddPic() {
log.info("galleryAddPic currentPhotoPath=" + currentPhotoPath);
// Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
// File file = new File(currentPhotoPath);
// //Uri contentUri = Uri.fromFile(file);
// Uri contentUri = FileProvider.getUriForFile(this, "com.example.activitytest.fileprovider", file);
// mediaScanIntent.setData(contentUri);
// this.sendBroadcast(mediaScanIntent);
mediaScannerConnection.connect();
}