天天看點

安卓調用系統相機拍照并儲存在手機中,适配7.0以上版本

引言:

調用系統相機拍照是安卓中比較常見,也比較簡單的功能。那麼拍照後得到的照片,我們有兩種處理:第一種,我們隻把它們顯示出來(不儲存在檔案中,但是這種隻能顯示縮略圖)。第二種,我們将照片寫入到檔案中,即儲存起來,那麼就可以重複使用。對于寫入到檔案這種操作,7.0以上版本需要特殊照顧,以避免出現FileUriExposedException異常。

第一種(擷取縮略圖):

首先,啟動系統相機:

Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
           

然後,重寫回調方法:

@Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode==REQUEST_IMAGE_CAPTURE&& resultCode == RESULT_OK){
            Bundle extras=data.getExtras();
            Bitmap imageBitmap = (Bitmap) extras.get("data");
            save(imageBitmap);
        }
    }
           

現在,imageBitmap就是我們的縮略圖,可以直接顯示在UI界面。其實,縮略圖也是可以儲存的,我這裡的save方法,就是将它寫入到外部存儲中。

/**
     * 處理系統相機拍照後對圖檔的儲存,但是實際上儲存的僅僅是一張縮略圖,有些時候并不能滿足需要
     * 而完整圖檔的擷取在7.0以上版本并不容易,用原始方法的話會報異常,
     * @param bitmap
     */
    private void save(Bitmap bitmap){
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化時間
        String filename = format.format(date) + ".jpg";
        File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        File folder=new File(dir,"my_pictures");
        /**
         * File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
         * getExternalStorageDirectory()方法已經廢棄,官方也不推薦使用,getExternalFilesDir是
         * 官方文檔中使用的API
         */

        if(!folder.exists()){
            folder.mkdir();
        }
        File file=new File(folder,filename);
        try {
            FileOutputStream outputStream=new FileOutputStream(file);
            bitmap.compress(Bitmap.CompressFormat.JPEG,100,outputStream);
            outputStream.flush();
            outputStream.close();
            Toast.makeText(this, "Take finish", Toast.LENGTH_SHORT).show();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        Log.w("Tag",file.getPath());  //列印存儲路徑
    }
           

一張縮略圖,放大了就會模糊,可能不太夠用。是以不建議這樣做,那麼下面開始第二種方法的講解。

第二種(存入到檔案中):

/**
     * 調用系統相機拍照并儲存的原始方法,7.0以上需要适配
     * 這裡涉及到的問題就是内容共享,要使用到Provider類
     */
    private void dispatchTakePictureIntent() {
        Date date = new Date();
        SimpleDateFormat format = new SimpleDateFormat("yyyyMMddHHmmss"); // 格式化時間
        String filename = format.format(date) + ".jpg";
        File dir=getExternalFilesDir(Environment.DIRECTORY_PICTURES);
        //以dir檔案為父級檔案夾建立子檔案
        File folder=new File(dir,"my_pictures");
        /**
         * File folder=new File(Environment.getExternalStorageDirectory()+"/my_pictures");
         * getExternalStorageDirectory()方法已經廢棄,官方也不推薦使用,getExternalFilesDir是
         * 官方文檔中使用的API
         */

        if(!folder.exists()){
        	//建立目錄
            folder.mkdir();
        }
        File file=new File(folder,filename);

        Uri uri=null;
        if(Build.VERSION.SDK_INT>=24){
            uri= FileProvider.getUriForFile(MainActivity.this,"com.example.camera.fileprovider",file);
        }else{
            uri=Uri.fromFile(file);
        }

        Log.w("Tag0",uri+"\n"+getExternalFilesDir(Environment.DIRECTORY_PICTURES));

        Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);

        if (takePictureIntent.resolveActivity(getPackageManager()) != null) {
            startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE);
        }
    }
           

我們發現對Intent的操作多了一行,takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT,uri);

這句話大概是将拍攝完成的照片存儲到對應uri的路徑,原來舊版很容易實作,定義好存儲路徑,直接一行代碼,通過檔案擷取路徑就可以了。7.0以上對内容共享做了限制,需要使用到Provider類,然後調用FileProvider類的方法擷取uri,下面給出适配的相關操作:

1.在res目錄下建立xml檔案夾,然後建立根節點為paths的路徑檔案:

file_paths.xml

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="my_images"
        path="/Android/data/com.example.camera/files/Pictures/my_pictures"/>
</paths>
           

external-path标簽就代表着外部存儲,path屬性相當于它之下的目錄,我們可以不再往下寫,直接寫一個"/",其實最終檔案存儲的路徑都一樣,隻是我們得到的Uri不同,那就來對比一下吧:

完整uri

安卓調用系統相機拍照并儲存在手機中,适配7.0以上版本

不完整uri

安卓調用系統相機拍照并儲存在手機中,适配7.0以上版本

列印的第一行是我們最終調用getUriForFile得到的uri,第二行是getExternalFilesDir(Environment.DIRECTORY_PICTURES)傳回的路徑。

2.在清單檔案中注冊provider:

<provider
            android:name="androidx.core.content.FileProvider"
            android:authorities="com.example.camera.fileprovider"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths"
                />
        </provider>
           

都是固定用法,注意authorities請使用"包名.fileprovider",貌似後面是什麼影響不大,但是包名必須寫對,在getUriForFile方法中要傳入authorities對應String型參數。

android:grantUriPermissions=“true”,不可以省略,必須為true,不然會報錯。

文獻參考:

Android Developers官方文檔:拍照

這裡面還介紹了如何将照片顯示在相冊(預設情況是不會直接顯示在相冊的),以及圖檔的解碼。