適配7.0拍照和相冊獲取圖片

1.調用相機android

private void takePhoto() {
    //建立File對象,用於存儲拍照後的圖片
    File outputImage = new File(getExternalCacheDir(), "output_iamge.jpg");
    try {
        if (outputImage.exists()) {
            outputImage.delete();
        }
        outputImage.createNewFile();
    } catch (IOException e) {
        e.printStackTrace();
    }
    if (Build.VERSION.SDK_INT >= 24) {
        imageUri = FileProvider.getUriForFile(this, "com.test.camera.fileprovider", outputImage);
    } else {
        imageUri = Uri.fromFile(outputImage);
    }
    //啓動相機程序
    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    startActivityForResult(intent, 3);
}

        首先這裏建立了一個File對象,用於存放攝像頭拍下的照片(output_image.jpg),並將他放在SD卡的應用緩存目錄下(指SD卡中專門用於存放當前應用緩存數據的位置),調用getExternalCacheDir()方法能夠獲得該目錄,具體路徑爲/sdcard/Android/data/<package name>/cache。爲何呢?應爲從Android6.0系統開始,讀寫SD卡被列爲危險權限,若是將圖片放在SD卡的任何其餘目錄,都要進行運行時權限處理,而是用應用關聯目錄則能夠跳過這一步。緩存

        接着進行一個判斷,若是運行設備的系統版本低於Android7.0,就調用Uri的fromFile()方法將File對象轉換成Uri對象,這個Uri對象標識着output_image.jpg這張圖片本地的真實路徑。不然,就調用FileProvider的getUriForFile()的方法將File對象轉換成一個封裝過的Uri對象。getUriForFile()第二個參數能夠是任意惟一的字符串。之因此進行這樣一層轉換,是由於從Android7.0系統開始,直接使用本地真實路徑的Uri被認爲是不安全,會拋出FileUriExposedException異常。而FileProvider則是一種特殊的內容提供器,它使用了和內容提供器相似的機制來對數據進行保護,能夠選擇性的將封裝過的Uri共享給外部,從而提升了應用的安全性。安全

        因爲咱們使用的是一個隱式Intent,系統會找出可以響應這個Intent的活動去啓動,這樣照相機程序就會啓動,拍下的照片就會輸出到output_image.jpg中。app

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case 3:
            if (resultCode == RESULT_OK) {
                try {
                    //將拍攝的照片顯示出來
                    Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                    img_img.setImageBitmap(bitmap);
                } catch (FileNotFoundException e) {
                    e.printStackTrace();
                }
            }
            break;
    }
}

       不過如今尚未結束,剛纔提到了內容提供器,咱們天然要在AndroidManifest.xml中對內容提供器進行註冊。ide

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="...">
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        ...
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="com.test.camera.fileprovider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
    </application>
</manifest>

        其中,android:name屬性的值是固定的,android:authorities屬性值必需要和剛纔FileProvider.getUriFile()方法中的第二個參數一致。另外,<provider>標籤內部使用<meta-data>來指定Uri的共享路徑,並引用了@xml/file_paths資源。下面咱們來建立它。ui

        res->New->Directory,建立一個xml目錄,接着xml->New->File,建立一個file_paths.xml文件。文件以下:this

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="my_images" path="" />
</paths>

        其中,external-path就是用來指定Uri共享的,name屬性的值隨便填,path屬性的值爲共享的具體路徑。這裏設置爲空就表示將整個SD卡進行共享,固然也能夠僅共享output——image.jpg這張圖片的路徑。code

        另外,Android4.4系統以前,訪問SD卡的應用關聯目錄也是要聲明權限的,從4.4系統開始就不須要權限的聲明。爲兼容老版本須要聲明該權限:xml

<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

2.調用相冊對象

        從相冊中讀取圖片,咱們先要動態申請WRITE_EXTERNAL_STORAGE這個危險權限。

String[] permissions = {Manifest.permission.WRITE_EXTERNAL_STORAGE};
private void getImage() {
    if (ContextCompat.checkSelfPermission(this, permissions[0]) != PackageManager.PERMISSION_GRANTED) {
        ActivityCompat.requestPermissions(this, permissions, 1);
    } else {
        openAlbum();
    }
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch (requestCode) {
        case 1:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                openAlbum();
            } else {
                Toast.makeText(this, "You denied the permission", Toast.LENGTH_SHORT).show();
            }
            break;
    }
}

        用戶受權成功後調用openAlbum()方法,調用相冊。

private void openAlbum() {
    Intent intent = new Intent("android.intent.action.GET_CONTENT");
    intent.setType("image/*");
    startActivityForResult(intent, 2);
}

        Android系統從4.4版本開始,選取相冊中的圖片再也不返回圖片真實的Uri,而是封裝過的Uri,所以在4.4版本以上的手機就須要對這個Uri解析。

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    switch (requestCode) {
        case 2:
            if (resultCode == RESULT_OK) {
                if (Build.VERSION.SDK_INT >= 19) {
                    handleImageOnKitKat(data);
                } else {
                    handleImageBeforeKitKat(data);
                }
            }
            break;
    }
}
@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
    String imagePath = null;
    Uri uri = data.getData();
    if (DocumentsContract.isDocumentUri(this, uri)) {
        //若是是document類型的uri,則經過document id 處理
        String docId = DocumentsContract.getDocumentId(uri);
        if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
            String id = docId.split(":")[1];//解析出數字格式的id
            String selection = MediaStore.Images.Media._ID + "=" + id;
            imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);
        } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) {
            Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));
            imagePath = getImagePath(contentUri, null);
        }
    } else if ("content".equalsIgnoreCase(uri.getScheme())) {
        //若是content類型的uri,則使用普通方式處理
        imagePath = getImagePath(uri, null);
    } else if ("file".equalsIgnoreCase(uri.getScheme())) {
        //若是是file類型的uri,直接獲取圖片路徑便可
        imagePath = uri.getPath();
    }
    displayImage(imagePath);
}

private void handleImageBeforeKitKat(Intent data) {
    Uri uri = data.getData();
    String imagePath = getImagePath(uri, null);
    displayImage(imagePath);
}

private String getImagePath(Uri uri, String selection) {
    String path = null;
    //經過Uri和selection來獲取真實的圖片路徑
    Cursor cursor = getContentResolver().query(uri, null, selection, null, null);
    if (cursor != null) {
        if (cursor.moveToFirst())
            path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA));
        cursor.close();
    }
    return path;
}

private void displayImage(String imagePath) {
    if (imagePath != null) {
        Bitmap bitmap = BitmapFactory.decodeFile(imagePath);
        img_img.setImageBitmap(bitmap);
    } else {
        Toast.makeText(this, "failed to get image", Toast.LENGTH_SHORT).show();
    }
}

        出自郭霖《第一行代碼 Android 第二版》第八章。

相關文章
相關標籤/搜索