我們很多時候需要進行圖片的裁剪,其實這個功能在android系統中已經有一套解決方案了,雖然界面和效果並不是很優秀但功能毫無疑問是完美實現了。至於,不用自帶的方案怎么做自定義,這個就是后話了。本篇主要講解的是裁剪的原理和流程,外帶分析了大圖裁剪和小圖裁剪的不同之處,同時給出具體的實現方案。
一、原理+流程
andorid提供了一個action,com.android.camera.action.CROP,
是Intent intent = new Intent("com.android.camera.action.CROP");
通過這個action就可以實現圖片的裁剪,具體就是實現這個intent,然后在這個intent中putExtra()中put各種參數,最后通過來啟動一個startActivityForResult(intent, requestCode);,這是裁剪圖片的activity,進行裁剪。
裁剪完后返回一個bitmap,交給開發者進行處理。
也就是說,我們是通過系統寫好的Activity進行了主要的操作,自己只需要在activity類中的onActivityResult中根據requestCode來進行判斷和處理即可。
啟動系統相冊的Activity:
/** * 從相冊獲取圖片 */ private void choicePicFromAlbum() { // 來自相冊 Intent albumIntent = new Intent(Intent.ACTION_PICK, null); /** * 下面這句話,與其它方式寫是一樣的效果,如果: * intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI); * intent.setType(""image/*");設置數據類型 * 要限制上傳到服務器的圖片類型時可以直接寫如:"image/jpeg 、 image/png等的類型" */ albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(albumIntent, ALBUM_OK); }
啟動系統照相的Activity:
/** * 拍照后獲取圖片 */ private void choicePicFromCamera() { // 來自相機 Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 下面這句指定調用相機拍照后的照片存儲的路徑,這樣通過這個uri就可以得到這個照片了 cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); startActivityForResult(cameraIntent, CAMERA_OK);// CAMERA_OK是用作判斷返回結果的標識 }
啟動裁剪圖片的Activity:
/** * 裁剪圖片方法實現 * @param uri */ public void clipPhoto(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); //可以選擇圖片類型,如果是*表明所有類型的圖片 intent.setDataAndType(uri, "image/*"); // 下面這個crop = true是設置在開啟的Intent中設置顯示的VIEW可裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是寬高的比例,這里設置的是正方形(長寬比為1:1) intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪圖片寬高 intent.putExtra("outputX", 1000); intent.putExtra("outputY", 1000); //裁剪時是否保留圖片的比例,這里的比例是1:1 intent.putExtra("scale", true); //是否是圓形裁剪區域,設置了也不一定有效 //intent.putExtra("circleCrop", true); //設置輸出的格式 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); //是否將數據保留在Bitmap中返回 intent.putExtra("return-data", true); startActivityForResult(intent, CUT_OK); }
二、主要問題
如果我們截取的圖片是大圖,那么我們首先會想着提高輸出圖片的大小
// outputX outputY 是裁剪圖片寬高 intent.putExtra("outputX", 800); intent.putExtra("outputY", 800);
但這樣就會出現問題,由於圖片過大,占用內存過多所以系統會自行將圖片進行壓縮,以避免出現OOM的問題。下面摘錄一篇博文的部分內容來解釋這個問題:
原文:http://blog.csdn.net/floodingfire/article/details/8144604
在Android中,Intent觸發Camera程序,拍好照片后,將會返回數據,但是考慮到內存問題,Camera不會將全尺寸的圖像返回給調用的Activity,一般情況下,有可能返回的是縮略圖,比如120*160px。
這是為什么呢?這不是一個Bug,而是經過精心設計的,卻對開發者不透明。以我的小米手機為例,攝像頭800W像素,根據我目前設置拍出來的圖片尺寸為3200*2400px。有人說,那就返回唄,大不了耗1-2M的內存,不錯,這個尺寸的圖片確實只有1.8M左右的大小。但是你想不到的是,這個尺寸對應的Bitmap會耗光你應用程序的所有內存。Android出於安全性考慮,只會給你一個寒磣的縮略圖。
在Android2.3中,默認的Bitmap為32位,類型是ARGB_8888,也就意味着一個像素點占用4個字節的內存。我們來做一個簡單的計算題:3200*2400*4 bytes = 30M。
如此驚人的數字!哪怕你願意為一張生命周期超不過10s的位圖願意耗費這么巨大的內存,Android也不會答應的。
1 |
Mobile devices typically have constrained system resources. |
2 |
Android devices can have as little as 16MB of memory available to a single application. |
這是Android Doc的原文,雖然不同手機系統的廠商可能圍繞16M這個數字有微微的上調,但是這30M,一般的手機還真揮霍不起。也只有小米這種牛機,內存堪比個人PC,本着土財主般揮金如土的霸氣才能做到。
得出的結論是,如果你截取的是小圖那么就返回一個bitmap,但如果是大圖那么就返回一個uri。這樣就不會出現問題啦。
三、從相冊中截圖
參考自:http://blog.csdn.net/floodingfire/article/details/8144615
現在原作者托管的代碼已經用了lib包作為例子了,和博客中略有差異。
原作者博文中寫了兩種方式,我個人用的不是很習慣。通過自己的測試發現博主提供的方法在手機上也沒法適用,於是貼出自己的解決方案。目前在小米2-原生4.4系統上測試通過
具體寫法參照:
http://www.cnblogs.com/tianzhijiexian/p/3989296.html
這個文章是參照博主的寫的,在miui上測試通過:http://www.cnblogs.com/tianzhijiexian/p/3859480.html,但之后就出問題了。