安卓調(diào)用系統(tǒng)相機拍照、從相冊選擇圖片并裁剪保存到SD卡(適配7.0)

最近在看郭神(郭霖)的第一行代碼(第二版),也就是大家說的第二行代碼,也算是鞏固一下自己的基礎(chǔ)吧(暴露 了自己基礎(chǔ)不好/??),現(xiàn)在已將看了一半,真的是收益良多??吹绞褂孟鄼C拍照的時候,自己 也就跟著demo敲了一下,覺得自己寫的拍照真的是不忍直視啊,哈哈,不過demo里面沒有裁剪并保存到SD卡的部分,所以這篇文章是結(jié)合郭神的demo跟自己的項目整理的一份代碼,先看下效果圖:</p>

最終效果圖
界面很簡單,兩個Button一個imageView。
接下來就是枯燥的代碼了,挑主要的說,源碼最后會有下載地址。
1.先看下點擊拍照,因為6.0以上安卓增加了權(quán)限管理,我們這里我先做了一個權(quán)限申請:
//檢查權(quán)限(6.0以上做權(quán)限判斷)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {    
    if (mPermissionsChecker.lacksPermissions(PERMISSIONS)) {        
        startPermissionsActivity();    
    } else {        
      openCamera();    
    }
   } else {    
      openCamera();
  }

如果有權(quán)限,則直接打開系統(tǒng)相機:

/** 
* 打開系統(tǒng)相機 
*/
private void openCamera() {    
   File file = new FileStorage().createIconFile();    
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {        
   imageUri = FileProvider.getUriForFile(MainActivity.this, "com.bugull.cameratakedemo.fileprovider", file);//通過FileProvider創(chuàng)建一個content類型的Uri    
   } else {        
   imageUri = Uri.fromFile(file);    
   }    
   Intent intent = new Intent();    
   if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {        
   intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加這一句表示對目標(biāo)應(yīng)用臨時授權(quán)該Uri所代表的文件    
   }    
   intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);//設(shè)置Action為拍照    
   intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//將拍取的照片保存到指定URI    
   startActivityForResult(intent, REQUEST_CAPTURE);}

這里指定保存圖片路徑的時候,我們做了一個判斷,如果運行設(shè)備的版本低于7.0,則調(diào)用Uri.fromFile(),可以獲取到圖片的本地真實路徑。否則,就調(diào)用FileProvider.getUriForFile(),獲取一個封裝過的uri對象,這是因為從安卓7.0開始,直接使用本地真實路徑被認(rèn)為是不安全的,會拋出FileUriExposedExCeption異常,而FileProvider則是一種特殊的內(nèi)容提供其,它使用了和內(nèi)容提供器類似的機制來對數(shù)據(jù)進行保護,可以選擇性地將封裝過的Uri共享給外部,從而提高了應(yīng)用的安全性。

上面我們提到了內(nèi)容提供器,作為安卓四大組件之一,肯定是要在xml中進行注冊的:

<provider    
   android:name="android.support.v4.content.FileProvider"    
   android:authorities="com.bugull.cameratakedemo.fileprovider"    
   android:grantUriPermissions="true"    
   android:exported="false">    
      <meta-data        
      android:name="android.support.FILE_PROVIDER_PATHS"        
      android:resource="@xml/file_paths" />
</provider>

provider標(biāo)簽里的 android:name的值是固定的。android:authorities的值要跟我們上面獲取uri的時候一直,這里我使用的是:包名.fileprovider。exported:要求必須為false,為true則會報安全異常。grantUriPermissions:true,表示授予 URI 臨時訪問權(quán)限。<meta-data />標(biāo)簽里面是用來指定共享的路徑。 android:resource="@xml/file_paths"就是我們的共享路徑配置的xml文件,


位置如圖所示

接下來看看這個file_paths是如何配置的

<?xml version="1.0" encoding="utf-8"?>
<paths    
   <paths>        
      <external-path path="demo" name="camera_photos" />    
   </paths>
</paths>

external-path就是用來指定Uri共享的,name屬性的值可以隨便填寫,path屬性的值表示共享的具體位置,設(shè)置為空,就表示共享整個SD卡,由于我在sd上保存圖片的時候創(chuàng)建了一個名字叫demo的文件夾,所以這里我寫一個demo就OK了,具體情況,自己再配置哈,這里我就不多提了。
拍照完成了就是裁剪了,我們看下裁剪的代碼:

/** 
* 裁剪 
*/
private void cropPhoto() {    
File file = new FileStorage().createCropFile();    
Uri outputUri = Uri.fromFile(file);    
Intent intent = new Intent("com.android.camera.action.CROP");    
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {        
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);    
}    
intent.setDataAndType(uri, "image/*");    
intent.putExtra("crop", "true");    
intent.putExtra("aspectX", 1);    
intent.putExtra("aspectY", 1);    
intent.putExtra("scale", true);    
intent.putExtra("return-data", false);    
intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);    
intent.putExtra("outputFormat", 
Bitmap.CompressFormat.JPEG.toString());    
intent.putExtra("noFaceDetection", true);     
startActivityForResult(intent, REQUEST_PICTURE_CUT);}
參數(shù) 數(shù)據(jù)類型 描述
crop String 發(fā)送裁剪信號
aspectX int X方向上的比例
aspectY int Y方向上的比例
outputX int 裁剪區(qū)的寬
outputY int 裁剪區(qū)的高
scale boolean 是否保留裁剪比例
scale return-data 是否將數(shù)據(jù)保留在bitmap中返回
MediaStore.EXTRA_OUTPUT URI 裁剪的圖片保存的地址
outputFormat String 圖片輸出格式
noFaceDetection boolean 是否取消人臉識別

調(diào)用手機拍照的差不多就是上面這些,下面我們?nèi)绾慰纯传@取系統(tǒng)相冊的圖片。

2.首先還是來獲取權(quán)限,上面已經(jīng)貼過代碼了,這里就不再貼出來啦!
下面看看如何打開系統(tǒng)相冊
/** 
* 從相冊選擇 
*/
private void selectFromAlbum() {    
Intent intent = new Intent(Intent.ACTION_PICK);    
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");    
startActivityForResult(intent, REQUEST_PICK_IMAGE);}
為了兼容老版本,下面我們做了一些判斷,如果系統(tǒng)是4.4以上的手機就調(diào)用handleImageOnKitKat(),否則就調(diào)用

handleImageBeforeKitKat(),之所以這樣子處理,是因為4.4之后選取中的圖片不再返回真實的Uri了,而是封裝過的Uri,所以在4.4以上,就要對這個Uri進行解析。

4.4以上的處理方式
@TargetApi(19)
private void handleImageOnKitKat(Intent data) {    
   imagePath = null;    
   imageUri = data.getData();    
   if (DocumentsContract.isDocumentUri(this, imageUri)) {        
   //如果是document類型的uri,則通過document id處理        
   String docId = DocumentsContract.getDocumentId(imageUri);        
   if("com.android.providers.media.documents".equals(imageUri.getAuthority())) {            
   String id = docId.split(":")[1];//解析出數(shù)字格式的id            
   String selection = MediaStore.Images.Media._ID + "=" + id;            
   imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection);        
} else if ("com.android.downloads.documents".equals(imageUri.getAuthority())) {           
   Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId));            
   imagePath = getImagePath(contentUri, null);        
}    
} else if ("content".equalsIgnoreCase(imageUri.getScheme())) {       
 //如果是content類型的Uri,則使用普通方式處理        
   imagePath = getImagePath(imageUri, null);    
} else if ("file".equalsIgnoreCase(imageUri.getScheme())) {        
//如果是file類型的Uri,直接獲取圖片路徑即可        
   imagePath = imageUri.getPath();    
}    
   cropPhoto();
}
4.4以下的處理方式
private void handleImageBeforeKitKat(Intent intent) {    
   imageUri = intent.getData();    
   imagePath = getImagePath(imageUri, null);    
   cropPhoto();
}

下面附上源碼,如果喜歡,記得點贊??,如有問題,歡迎指正?。。〈蠹乙黄疬M步。
源碼下載地址:
https://pan.baidu.com/s/1Ot7YUuR2ICQOZMqN0kbmbw

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容