Android調(diào)用系統(tǒng)圖庫和相機(jī)獲取圖片并裁剪

最近用到從系統(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é)束士鸥。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末闲孤,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子烤礁,更是在濱河造成了極大的恐慌讼积,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,265評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脚仔,死亡現(xiàn)場離奇詭異币砂,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)玻侥,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,078評論 2 385
  • 文/潘曉璐 我一進(jìn)店門决摧,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事掌桩”咚” “怎么了?”我有些...
    開封第一講書人閱讀 156,852評論 0 347
  • 文/不壞的土叔 我叫張陵波岛,是天一觀的道長茅坛。 經(jīng)常有香客問我,道長则拷,這世上最難降的妖魔是什么贡蓖? 我笑而不...
    開封第一講書人閱讀 56,408評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮煌茬,結(jié)果婚禮上斥铺,老公的妹妹穿的比我還像新娘。我一直安慰自己坛善,他們只是感情好晾蜘,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,445評論 5 384
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著眠屎,像睡著了一般剔交。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上改衩,一...
    開封第一講書人閱讀 49,772評論 1 290
  • 那天岖常,我揣著相機(jī)與錄音,去河邊找鬼葫督。 笑死腥椒,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的候衍。 我是一名探鬼主播,決...
    沈念sama閱讀 38,921評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼洒放,長吁一口氣:“原來是場噩夢啊……” “哼蛉鹿!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起往湿,我...
    開封第一講書人閱讀 37,688評論 0 266
  • 序言:老撾萬榮一對情侶失蹤妖异,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后领追,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體他膳,經(jīng)...
    沈念sama閱讀 44,130評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,467評論 2 325
  • 正文 我和宋清朗相戀三年绒窑,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了棕孙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,617評論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖蟀俊,靈堂內(nèi)的尸體忽然破棺而出钦铺,到底是詐尸還是另有隱情,我是刑警寧澤肢预,帶...
    沈念sama閱讀 34,276評論 4 329
  • 正文 年R本政府宣布矛洞,位于F島的核電站,受9級特大地震影響烫映,放射性物質(zhì)發(fā)生泄漏沼本。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,882評論 3 312
  • 文/蒙蒙 一锭沟、第九天 我趴在偏房一處隱蔽的房頂上張望抽兆。 院中可真熱鬧,春花似錦冈钦、人聲如沸郊丛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,740評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽厉熟。三九已至,卻和暖如春较幌,著一層夾襖步出監(jiān)牢的瞬間揍瑟,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,967評論 1 265
  • 我被黑心中介騙來泰國打工乍炉, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留绢片,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,315評論 2 360
  • 正文 我出身青樓岛琼,卻偏偏與公主長得像底循,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子槐瑞,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,486評論 2 348

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