前言
我們在Android開發中經常會需要使用相機或者從相冊中選取圖片的情況,今天就把這里面相關的知識點總結下,方便以后開發的時候使用。
1、相機拍照並可自定義截圖功能
我們先來看如何使用Intent來打開照相機,相信這段代碼大伙應該很熟悉了。代碼如下:
1 //打開照相機,進行拍照 2 intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 3 //設置照片的臨時保存路徑 4 intent.putExtra(MediaStore.EXTRA_OUTPUT, mTempImageUri); 5 startActivityForResult(intent, TAKE_PHOTO_BY_CAMERA);
這段代碼中我們看到,對於第四行設置了拍照后圖片的保存路徑。這個我們可以自定義的。然后我們通過startActivityForResult來打開相機,並接受相關的數據。
下面我們來看下相機拍攝后的處理邏輯。代碼如下:
1 /** 2 * 處理照相或者相冊返回的圖片數據 3 * @param requestCode 4 * @param resultCode 5 * @param data 6 */ 7 @Override 8 protected void onActivityResult(int requestCode, int resultCode, Intent data) { 9 if(resultCode==Activity.RESULT_OK){ 10 switch (requestCode){ 11 //處理相機 12 case TAKE_PHOTO_BY_CAMERA: 13 if(mCrop) { 14 //對指定路徑下的圖片進行裁剪 15 cropCameraImage(mTempImageUri, DEFAULT_CROP_X, DEFAULT_CROP_Y, CROP_PHOTO); 16 }else { 17 mBitmap = getThumbnail(mTempImageUri, mImageWidth, mImageHeight); 18 setResult(true); 19 } 20 break; 21 //處理相冊 22 case SELECT_PHOTO_BY_ALBUM: 23 Uri selectedPhotoUri=data.getData(); 24 //對圖片進行裁剪 25 if(mCrop) { 26 cropAlbumImage(selectedPhotoUri, mTempImageUri, DEFAULT_CROP_X, DEFAULT_CROP_Y, CROP_PHOTO); 27 }else { 28 //否則就將圖片壓縮成指定大小返回 29 mBitmap = getThumbnail(selectedPhotoUri, mImageWidth, mImageHeight); 30 setResult(true); 31 } 32 break; 33 //裁剪圖片 34 case CROP_PHOTO: 35 if(data!=null) { 36 /** 37 * 如果想使用data.getParcelableExtra("data")獲取bitmap 38 * 只需要在裁剪時設置return-data為true即可 39 */ 40 if(mReturnData){ 41 mBitmap = data.getParcelableExtra("data"); 42 if (mBitmap == null) { 43 setResult(false); 44 } 45 }else { 46 Uri originalUri = data.getData(); 47 if (originalUri == null) { 48 originalUri = mTempImageUri; 49 } 50 //獲取縮略圖 51 mBitmap = getThumbnail(originalUri, mImageWidth, mImageHeight); 52 } 53 } 54 setResult(true); 55 break; 56 default: 57 break; 58 } 59 }else { 60 setResult(false); 61 } 62 super.onActivityResult(requestCode, resultCode, data); 63 }
我們來看代碼的12-20行,我們看到這段代碼會產生一個分支,如果crop為true,那么會調用cropCameraImage方法對拍攝的圖片進行截圖。否則的話就返回指定大小的圖片(bitmap)給調用者。我們分開來看。
1、拍照並截圖。相關代碼如下:
1 /** 2 * 對照相機拍攝的圖片進行裁剪 3 * @param uri 4 * @param outputX 5 * @param outputY 6 * @param requestCode 7 */ 8 private void cropCameraImage(Uri uri, int outputX, int outputY, int requestCode){ 9 //相機拍攝的圖片像素較高,不使用return-data屬性使用Uri返回 10 mReturnData=false; 11 cropImage(uri,uri,outputX,outputY,mReturnData,requestCode); 12 }
我們看到里面調用了cropImage方法(從相冊取圖也使用了這個方法)。由於現在相機像素都很高,生成的圖片很大,往往有5M+的大小。不建議使用return-data屬性,這樣會占用很大的內存。我們這邊使用Uri。下面會詳細介紹。
cropImage方法的介紹。
1 /** 2 * 3 * @param sourceUri 4 * @param outputUri 5 * @param outputX 6 * @param outputY 7 * @param returnData 8 * @param requestCode 9 */ 10 private void cropImage(Uri sourceUri,Uri outputUri,int outputX,int outputY,boolean returnData,int requestCode) { 11 try { 12 /** 13 * 下面解釋各個參數的詳細信息 14 */ 15 Intent intent = new Intent("com.android.camera.action.CROP"); 16 intent.setDataAndType(sourceUri, "image/*"); 17 //發送裁剪信號 18 intent.putExtra("crop", "true"); 19 //X,Y方向上的比例(1:1就是正方形,2:1就是長方形) 20 intent.putExtra("aspectX", 1); 21 intent.putExtra("aspectY", 1); 22 //裁剪區的寬,高 23 intent.putExtra("outputX", outputX); 24 intent.putExtra("outputY", outputY); 25 //是否保留比例 26 intent.putExtra("scale", true); 27 if(returnData) { 28 //是否將數據保留在Bitmap中返回(注意:小圖片可以使用return-data:true的設置,大圖片的話建議使用extra_output) 29 intent.putExtra("return-data", returnData); 30 }else { 31 //設置裁剪后的圖片路徑覆蓋相機拍攝圖片路徑 32 intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri); 33 } 34 //裁剪后圖片的后綴名 35 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); 36 //沒有人臉檢測 37 intent.putExtra("noFaceDetection", true); 38 startActivityForResult(intent, requestCode); 39 } catch (Exception ex) { 40 setResult(false); 41 } 42 }
在代碼的27-32行我們看到通過returnData我們設置是否使用return-data屬性。設置了return-data屬性,Android系統會自動返回bitmap對象給我們,如:onActivityResult中的data.getParcelableExtra這種方法。這樣對於小圖片當然非常方便。但是對於大圖片會造成內存消耗過高,應用程序產生異常。在最后我們看cropImage方法也是調用了startActivityForResult方法,只不過它的參數是CROP_PHOTO。
2、直接生成指定大小的圖片
下面我們來介紹不使用截圖,直接將生成指定大小圖片並返回的方法,即代碼中的getThumbnail方法。這一段代碼是可以實現將圖片縮小成指定長寬的尺寸。代碼如下:
1 /** 2 * @param uri 3 * @param width 4 * @param height 5 * @return 6 * @throws IOException 7 */ 8 public Bitmap getThumbnail(Uri uri,int width,int height){ 9 Bitmap bitmap=null; 10 InputStream input=null; 11 try { 12 ContentResolver resolver = getContentResolver(); 13 input = resolver.openInputStream(uri); 14 BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options(); 15 /** 16 * 如果設置inJustDecodeBounds為true,仍可以獲取到bitmap信息,但完全不用分配內存,因為沒有獲取像素,所以我們可以利用得到的Bitmap的大小, 17 * 重新壓縮圖片,然后在內存中生成一個更小的Bitmap,這樣即便是一個4MB的JPG,我們也可以隨心所欲地把他壓縮到任意大小,從而節省了內存 18 */ 19 onlyBoundsOptions.inJustDecodeBounds = true; 20 onlyBoundsOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; 21 BitmapFactory.decodeStream(input, null, onlyBoundsOptions); 22 if(input!=null) { 23 input.close(); 24 } 25 if ((onlyBoundsOptions.outWidth == -1) || (onlyBoundsOptions.outHeight == -1)) { 26 return bitmap; 27 } 28 BitmapFactory.Options bitmapOptions = new BitmapFactory.Options(); 29 /** 30 * 計算Bitmap的縮放比例 31 */ 32 bitmapOptions.inSampleSize = calculateInSampleSize(onlyBoundsOptions, width, height); 33 bitmapOptions.inPreferredConfig = Bitmap.Config.ARGB_8888; 34 input = resolver.openInputStream(uri); 35 bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions); 36 }catch (Exception ex){ 37 return bitmap; 38 }finally { 39 if(input!=null){ 40 try { 41 input.close(); 42 }catch (IOException ioEx){} 43 } 44 } 45 return bitmap; 46 }
這種方法的原理可以參考我的另一篇文章:http://www.cnblogs.com/dreamGong/p/5255528.html。通過介紹我們知道了相機拍攝圖片並且進行截圖和指定大小圖片獲取的相關方法。相信聰明的你也一定知道從相冊取圖肯定也是類似的。的確,從上面onActivityResult方法中我們看到從相冊取圖也是大同小異的。最后我再來介紹下onActivityResult方法中的CROP_PHOTO的相關邏輯。代碼如下:
1 /** 2 * 如果想使用data.getParcelableExtra("data")獲取bitmap 3 * 只需要在裁剪時設置return-data為true即可 4 */ 5 if(mReturnData){ 6 mBitmap = data.getParcelableExtra("data"); 7 if (mBitmap == null) { 8 setResult(false); 9 } 10 }else { 11 Uri originalUri = data.getData(); 12 if (originalUri == null) { 13 originalUri = mTempImageUri; 14 } 15 //獲取縮略圖 16 mBitmap = getThumbnail(originalUri, mImageWidth, mImageHeight); 17 }
我們看到如果我們設置了retuan-data屬性,是可以使用getParcelabelExtra方法來直接獲取bitmap的。如果我們設置return-data為false。設置intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);cropImage方法的29行。我們可以通過獲取Uri,然后重新生成一張bitmap返回給調用者。
參考文檔
https://my.oschina.net/ryanhoo/blog/86842
附整個代碼的邏輯