最近用到從系統(tǒng)圖庫和相機(jī)獲取圖片并裁剪當(dāng)頭像胡嘿,根據(jù)郭霖大神的第一行代碼調(diào)用相機(jī)和圖冊按傅,來進(jìn)行擴(kuò)展和總結(jié)含末。
1灸异、獲取權(quán)限
2府适、點(diǎn)擊按鈕來提示選擇圖庫還是相機(jī)
private String[]mCustomItems=new String[]{"本地相冊","相機(jī)拍照"};
//顯示選擇相機(jī),圖庫對話框
? ? private void showDialogCustom(){
? ? ? ? //創(chuàng)建對話框
? ? ? ? AlertDialog.Builder builder=new AlertDialog.Builder(MainActivity.this);
? ? ? ? builder.setTitle("請選擇:");
? ? ? ? builder.setItems(mCustomItems, new DialogInterface.OnClickListener() {
? ? ? ? ? ? @Override
? ? ? ? ? ? public void onClick(DialogInterface dialog, int which) {
? ? ? ? ? ? ? ? if(which==0) {
? ? ? ? ? ? ? ? ? ? //相冊
? ? ? ? ? ? ? ? }else if(which==1){
? ? ? ? ? ? ? ? ? ? //照相機(jī)
? ? ? ? ? ? ? ? }
? ? ? ? ? ? }
? ? ? ? });
? ? ? ? builder.create().show();
? ? }
3肺樟、相機(jī)拍照
//拍照后照片的Uri
private Uri imageUri;
//返回碼檐春,相機(jī)private static final int RESULT_CAMERA=200;
//先驗(yàn)證手機(jī)是否有sdcard
? ? ? ? ? ? ? ? ? ? String status=Environment.getExternalStorageState();
if(status.equals(Environment.MEDIA_MOUNTED))
? ? ? ? ? ? ? ? ? ? {
//創(chuàng)建File對象,用于存儲拍照后的照片
File outputImage=newFile(getExternalCacheDir(),"out_image.jpg");//SD卡的應(yīng)用關(guān)聯(lián)緩存目錄
try{
if(outputImage.exists()){
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? outputImage.delete();
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? ? ? ? ? outputImage.createNewFile();
Intent intent=newIntent(MediaStore.ACTION_IMAGE_CAPTURE);
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
imageUri=FileProvider.getUriForFile(MainActivity.this,
"com.hanrui.android.fileprovider",outputImage);//添加這一句表示對目標(biāo)應(yīng)用臨時(shí)授權(quán)該Uri所代表的文件
}else{
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? imageUri=Uri.fromFile(outputImage);
? ? ? ? ? ? ? ? ? ? ? ? ? ? }
//啟動(dòng)相機(jī)程序
intent.putExtra(MediaStore.Images.Media.ORIENTATION,0);
? ? ? ? ? ? ? ? ? ? ? ? ? ? intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
? ? ? ? ? ? ? ? ? ? ? ? ? ? startActivityForResult(intent,RESULT_CAMERA);
}catch(Exception e) {
// TODO Auto-generated catch block
Toast.makeText(MainActivity.this,"沒有找到儲存目錄",Toast.LENGTH_LONG).show();
? ? ? ? ? ? ? ? ? ? ? ? }
}else{
Toast.makeText(MainActivity.this,"沒有儲存卡",Toast.LENGTH_LONG).show();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? ? ? dialog.dismiss();
應(yīng)用關(guān)聯(lián)緩存目錄指SD卡中專門用于存放當(dāng)前應(yīng)用緩存數(shù)據(jù)的位置么伯,調(diào)用getExternalCacheDir()方法可以得到這個(gè)目錄疟暖,具體路徑是/sdcard/Android/data//cache。為什么要存放這個(gè)目錄呢?因?yàn)閺腁ndroid6.0系統(tǒng)開始俐巴,讀寫SD卡被列為危險(xiǎn)權(quán)限骨望,若將圖片存放在SD卡的任何其他目錄,都要進(jìn)行權(quán)限處理才行欣舵,使用應(yīng)用關(guān)聯(lián)目錄可以跳過這一步擎鸠。
接著進(jìn)行一個(gè)判斷,若設(shè)備版本低于Android7.0缘圈,就調(diào)用Uri的fromFile()方法將File對象轉(zhuǎn)換成Uri對象劣光,這個(gè)Uri對象標(biāo)識圖片的本地真實(shí)路徑。否則糟把,就調(diào)用FileProvider的getUriForFile()方法將File對象轉(zhuǎn)換成一個(gè)封裝過的Uri對象绢涡。getUriForFile()方法接收3個(gè)參數(shù),第一個(gè)要求傳入Context對象遣疯,第二個(gè)可以是任意字符串雄可,第三個(gè)是我們剛剛創(chuàng)建的File對象。進(jìn)行這樣一層轉(zhuǎn)換缠犀,因?yàn)閺腁ndroid7.0開始滞项,直接使用本地真實(shí)路徑的Uri被認(rèn)為是不安全的,會(huì)拋出一個(gè)FileUriExposedException異常夭坪。而FileProvider則是一種特殊的內(nèi)容提供器,它使用了和內(nèi)容提供器類似的機(jī)制來對數(shù)據(jù)進(jìn)行保護(hù)过椎,可以選擇性的將封裝過的Uri共享給外部室梅,從而提高了應(yīng)用的安全性。
在AndroidManifest.xml中對內(nèi)容提供器進(jìn)行注冊
...>
.....>
....
? ? ? ?
android:name屬性值是固定的疚宇,android:authorities屬性的值必須要和剛才FileProvider.getUriForFile()方法中的第二個(gè)參數(shù)一致亡鼠。另外,在標(biāo)簽的內(nèi)部使用來指定Uri的共享路徑敷待,并引用了一個(gè)@xml/file_paths資源间涵,下面創(chuàng)建這個(gè)資源:
在res目錄下創(chuàng)建一個(gè)xml目錄,在xml目錄下創(chuàng)建一個(gè)file_paths.xml文件榜揖,內(nèi)容如下:
? ?
external-path就是用來指定Uri共享的勾哩,name屬性的值可以隨便填,path屬性的值表示共享的具體路徑举哟,設(shè)置空值表示將整個(gè)SD卡進(jìn)行共享思劳。當(dāng)然也可以僅共享我們存放照片的路徑。
回調(diào)方法對圖片進(jìn)行裁剪
protectedvoidonActivityResult(intrequestCode,intresultCode, Intent data){
switch(requestCode){
caseRESULT_CAMERA:
if(resultCode==RESULT_OK){
//進(jìn)行裁剪
? ? ? ? ? ? ? ? ? ? startPhotoZoom(imageUri);
? ? ? ? ? ? ? ? }? ?
}
}
4妨猩、調(diào)用圖庫
//返回碼潜叛,本地圖庫private static final int RESULT_IMAGE=100;
//相冊
if(ContextCompat.checkSelfPermission(MainActivity.this,Manifest.permission.WRITE_EXTERNAL_STORAGE)!=
? ? ? ? ? ? ? ? ? ? ? ? ? ? PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this,newString[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}else{
? ? ? ? ? ? ? ? ? ? ? ? openAlbum();
? ? ? ? ? ? ? ? ? ? }
打開相冊
privatevoidopenAlbum(){
Intent intent=newIntent("android.intent.action.GET_CONTENT");
intent.setType("image/*");
startActivityForResult(intent,RESULT_IMAGE);//打開相冊
? ? }
@Override
publicvoidonRequestPermissionsResult(intrequestCode, @NonNull String[] permissions, @NonNullint[] grantResults){
switch(requestCode){
case1:
if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED){
? ? ? ? ? ? ? ? ? ? openAlbum();
}else{
Toast.makeText(this,"你沒有開啟權(quán)限",Toast.LENGTH_SHORT).show();
? ? ? ? ? ? ? ? }
break;
default:
break;
? ? ? ? }
? ? }
調(diào)用相冊時(shí)先進(jìn)行一個(gè)運(yùn)行時(shí)權(quán)限處理,動(dòng)態(tài)申請WRITE_EXTERNAL_STORAGE這個(gè)危險(xiǎn)權(quán)限。因?yàn)橄鄡哉掌娣旁赟D卡中威兜,從SD卡讀取照片需要申請這個(gè)權(quán)限销斟。WRITE_EXTERNAL_STORAGE表示同時(shí)授予程序?qū)D卡讀和寫的能力。
回調(diào)方法
@Override
protectedvoidonActivityResult(intrequestCode,intresultCode, Intent data){
caseRESULT_IMAGE:
if(resultCode==RESULT_OK&&data!=null){
//判斷手機(jī)系統(tǒng)版本號
if(Build.VERSION.SDK_INT>=19){
//4.4及以上系統(tǒng)使用這個(gè)方法處理圖片
? ? ? ? ? ? ? ? ? ? ? ? handlerImageOnKitKat(data);
}else{
//4.4以下系統(tǒng)使用這個(gè)方法處理圖片
? ? ? ? ? ? ? ? ? ? ? ? handlerImageBeforeKitKat(data);
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
break;
}
Android系統(tǒng)從4.4版本開始椒舵,選取相冊中圖片不再返回圖片真實(shí)的Uri了蚂踊,而是一個(gè)封裝過的Uri,隱因此4.4版本以上要對這個(gè)Uri進(jìn)行解析逮栅。
privatevoidhandlerImageOnKitKat(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];//解析出數(shù)字格式的id
String selection=MediaStore.Images.Media._ID+"="+id;
? ? ? ? ? ? ? ? imagePath=getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection);
}elseif("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);
? ? ? ? ? ? }
}elseif("content".equalsIgnoreCase(uri.getScheme())){
//如果是content類型的URI悴势,則使用普通方式處理
imagePath=getImagePath(uri,null);
}elseif("file".equalsIgnoreCase(uri.getScheme())){
//如果是file類型的Uri,直接獲取圖片路徑即可
? ? ? ? ? ? imagePath=uri.getPath();
? ? ? ? }
? ? ? ? startPhotoZoom(uri);
? ? }
privateStringgetImagePath(Uri uri, String selection){
String path =null;
//通過Uri和selection來獲取真實(shí)的圖片路徑
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();
? ? ? ? }
returnpath;
? ? }
如果返回的Uri是document類型的話,就取出doculent_id進(jìn)行處理措伐,若不是特纤,就是用普通方法處理。另外侥加,若Uri的authority是media格式的話捧存,documentid還需要進(jìn)行一次解析,要通過字符串分割的方式取出后半部分才能得到真正的數(shù)字id担败。取出的id用于構(gòu)建新的Uri和條件語句昔穴,然后把這些值作為參數(shù)傳入到getImagePath()方法中,就可以獲取圖片的真實(shí)路徑了提前。
privatevoidhandlerImageBeforeKitKat(Intent data){
? ? ? ? Uri cropUri=data.getData();
? ? ? ? startPhotoZoom(cropUri);
? ? }
直接取出Uri進(jìn)行裁剪吗货。
5、圖片裁剪的方法
private static final int CROP_PICTURE =2;//裁剪后圖片返回碼//裁剪圖片存放地址的Uriprivate UricropImageUri;
publicvoidstartPhotoZoom(Uri uri){
File CropPhoto=newFile(getExternalCacheDir(),"crop_image.jpg");
try{
if(CropPhoto.exists()){
? ? ? ? ? ? ? ? CropPhoto.delete();
? ? ? ? ? ? }
? ? ? ? ? ? CropPhoto.createNewFile();
}catch(IOException e){
? ? ? ? ? ? e.printStackTrace();
? ? ? ? }
? ? ? ? cropImageUri=Uri.fromFile(CropPhoto);
Intent intent =newIntent("com.android.camera.action.CROP");
intent.setDataAndType(uri,"image/*");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//添加這一句表示對目標(biāo)應(yīng)用臨時(shí)授權(quán)該Uri所代表的文件
? ? ? ? }
// 下面這個(gè)crop=true是設(shè)置在開啟的Intent中設(shè)置顯示的VIEW可裁剪
intent.putExtra("crop","true");
intent.putExtra("scale",true);
intent.putExtra("aspectX",1);
intent.putExtra("aspectY",1);
intent.putExtra("outputX",300);
intent.putExtra("outputY",300);
intent.putExtra("return-data",false);
? ? ? ? intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection",true);// no face detection
? ? ? ? startActivityForResult(intent, CROP_PICTURE);
? ? }
附加選項(xiàng)數(shù)據(jù)類型描述
cropString發(fā)送裁剪信號
aspectXintX方向上的比例
aspectYintY方向上的比例
outputXint裁剪區(qū)的寬
outputYint裁剪區(qū)的高
scaleboolean是否保留比例
return-databoolean是否將數(shù)據(jù)保留在Bitmap中返回
dataParcelable相應(yīng)的Bitmap數(shù)據(jù)
circleCropString圓形裁剪區(qū)域狈网?
MediaStore.EXTRA_OUTPUT ("output")URI將URI指向相應(yīng)的file:///...
@Override
protectedvoidonActivityResult(intrequestCode,intresultCode, Intent data){
caseCROP_PICTURE:// 取得裁剪后的圖片
if(resultCode==RESULT_OK) {
try{
? ? ? ? ? ? ? ? ? ? ? ? Bitmap headShot = BitmapFactory.decodeStream(getContentResolver().openInputStream(cropImageUri));
? ? ? ? ? ? ? ? ? ? ? .....
}catch(FileNotFoundException e) {
? ? ? ? ? ? ? ? ? ? ? ? e.printStackTrace();
? ? ? ? ? ? ? ? ? ? }
? ? ? ? ? ? ? ? }
break;
default:
break;
? ? ? ? }
? ? }
到此宙搬,獲得裁剪圖片結(jié)束。
還有一個(gè)問題:三星手機(jī)自帶的相機(jī)會(huì)轉(zhuǎn)屏而引起當(dāng)前activity銷毀拓哺,重建勇垛。
我們可以在當(dāng)前Activity的manifest文件里添加這一行代碼:
android:configChanges="orientation|keyboardHidden| screenSize"
關(guān)鍵就是添加screenSize
然后在Activityl里添加以下代碼:
@Override
publicvoidonConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
? ? }
好了,到此結(jié)束士鸥。