天天看點

Android 四大元件之 Activity_ 擷取拍照結果(startActivityForResult &onActivityResult)

通過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();
    }
           

繼續閱讀