一.概述
前段時(shí)間,寫(xiě)了或者間接寫(xiě)了幾個(gè)項(xiàng)目,然后一直不是很滿意拍照功能,圖片大然而效果還不好。乘著空檔期,我搜索了網(wǎng)上的資料,這邊根據(jù)拍照上傳的順序分析其中問(wèn)題:
1.根據(jù)onActivityResult返回值獲取小圖和大圖的問(wèn)題;
2.大圖小圖的代碼分析;
3.拍照得到過(guò)大圖片問(wèn)題(如何壓縮,六種方式);
4.具體Demo;
二.分析
1.大家都知道了我們需要通過(guò)隱式Intent到系統(tǒng)拍照頁(yè)面(當(dāng)然可以自定義,下篇再講),講到這里大家就都知道了一個(gè)Activity之間的回調(diào)方法onActivityResult(),在這個(gè)方法中因?yàn)槭峭ㄟ^(guò)Intnet攜帶一下三個(gè)參數(shù)來(lái)實(shí)現(xiàn)的:
前面兩個(gè)用于匹配返回的,因?yàn)楫?dāng)前Activity可能會(huì)通向多個(gè)Activity,要用這兩個(gè)參數(shù)來(lái)作區(qū)分,最后這個(gè)data根據(jù)字面意思就可以知道里面帶有好東西,Bundle bundle = data.getExtras();通過(guò)這個(gè)方法就可以獲取到了,Bitmap bitmap = (Bitmap) bundle.get("data");然后通過(guò)這個(gè)就可以獲取到bitmap,后面的事情想必大家都已經(jīng)知道了,在你的Activity中為所欲為吧,看下這個(gè)強(qiáng)制轉(zhuǎn)換類型,大家可以去試試bundle獲取到的具體是什么,會(huì)有意外收獲。
有了上面的鋪墊,我們來(lái)來(lái)說(shuō)下大圖和小圖的區(qū)別,其實(shí)很簡(jiǎn)單,獲取小圖就是可以直接從bundle.get("data");中獲取,但是這個(gè)對(duì)應(yīng)可能會(huì)讓你失望,因?yàn)锽undle中能存放的東西有限,所以會(huì)傳回壓縮圖,圖片那是相當(dāng)?shù)牟磺宄苑Q之為“小圖”。
接下來(lái)說(shuō)說(shuō)大圖,所謂的大圖就是獲取存在本地的圖片,不用系統(tǒng)傳回的那我們就用自己的,用自己的就需要路徑保存下來(lái),然后你就隨便的壓縮再顯示,任意揮霍你的任性。
以上就是大小圖的區(qū)別,接下來(lái)分析細(xì)節(jié)和代碼;
2.小圖的代碼:
newView.OnClickListener() {
@Override
public voidonClick(View v) {
Intent intent =newIntent(CameraPhotoActivity.this, ContractIconActivity.class);
Bundle b =newBundle();
b.putParcelable("bitmap",bitmap);
intent.putExtras(b);
startActivity(intent);
}
}
@Override
protected voidonActivityResult(intrequestCode,intresultCode, Intent data) {
//TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
// 判斷是否返回值
if(resultCode ==RESULT_OK) {
// 判斷返回值是否正確
if(requestCode ==CODE) {
// 獲取圖片
Bundle bundle = data.getExtras();
// 轉(zhuǎn)換圖片的二進(jìn)制流
Bitmap bitmap = (Bitmap) bundle.get("data");
// 設(shè)置圖片
iv_photo.setImageBitmap(bitmap);
// 加載原圖
}
}
}
小圖的獲取就是以上的代碼沒(méi)什么特別的接下來(lái)是大圖的:
// 記錄文件保存位置
private String mFilePath;
private FileInputStream is = null;
private Bitmap bitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
// 獲取SD卡路徑
mFilePath = Environment.getExternalStorageDirectory().getPath();
// 文件名
mFilePath = mFilePath + "/" + "photo.png";
}
new View.OnClickListener() {
@Override
public void onClick(View v) {
// 指定拍照
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
// 加載路徑
Uri uri = Uri.fromFile(new File(mFilePath));
// 指定存儲(chǔ)路徑,這樣就可以保存原圖了
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
// 拍照返回圖片
startActivityForResult(intent, CODEBIG);
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// TODO Auto-generated method stub
super.onActivityResult(requestCode, resultCode, data);
// 判斷是否返回值
if (resultCode == RESULT_OK) {
// 判斷返回值是否正確
if (requestCode == CODEBIG) {
// // 獲取輸入流
// is = new FileInputStream(mFilePath);
// // 把流解析成bitmap
// bitmap = BitmapFactory.decodeStream(is);
bitmap = BitmapFactory.decodeFile(mFilePath);
// 設(shè)置圖片
iv_photo_big.setImageBitmap(bitmap);
try {
FileOutputStream out = new FileOutputStream(mFilePath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 20, out);
out.flush();
out.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
這里我們先會(huì)定義一個(gè)mFilePath用來(lái)給拍照的本地保存圖片命名,然后路徑的話你們隨意,為了方便我就放在最外層方便查看了,這樣一來(lái),拍完照片之后就會(huì)存放在本地了,之后在onActivityResult中就可以通過(guò)這個(gè)路徑獲取到照片了,然后你又可以隨便玩弄它了,這邊我注釋了一種方法,他也是轉(zhuǎn)成Bitmap的一種方法。
重點(diǎn)在try()中,現(xiàn)創(chuàng)建輸出流,用bitmap.compress(Bitmap.CompressFormat.JPEG, 20, out);進(jìn)行解壓(這是解壓方式之一),注意了在方法中的圖片格式不能為PNG,PNG是不可壓縮文件,準(zhǔn)確的說(shuō)是不可以通過(guò)這種方式壓縮的文件,所以我在這邊用了JPEG的格式,第二個(gè)參數(shù)就是壓縮率(100-20)%,這樣就壓縮了80%;
??一一>在文件夾中的圖片大小在這個(gè)步驟之后就會(huì)變化了(這個(gè)很重要)。
3.這個(gè)話題轉(zhuǎn)自:http://blog.csdn.net/harryweasley/article/details/51955467
android中圖片是以bitmap形式存在的,那么bitmap所占內(nèi)存,直接影響到了應(yīng)用所占內(nèi)存大小,首先要知道bitmap所占內(nèi)存大小計(jì)算方式:
圖片長(zhǎng)度 x 圖片寬度 x 一個(gè)像素點(diǎn)占用的字節(jié)數(shù)
以下是圖片的壓縮格式:
其中,A代表透明度;R代表紅色;G代表綠色;B代表藍(lán)色。
ALPHA_8 表示8位Alpha位圖,即A=8,一個(gè)像素點(diǎn)占用1個(gè)字節(jié),它沒(méi)有顏色,只有透明度 ARGB_4444 表示16位ARGB位圖,即A=4,R=4,G=4,B=4,一個(gè)像素點(diǎn)占4+4+4+4=16位,2個(gè)字節(jié) ARGB_8888 表示32位ARGB位圖,即A=8,R=8,G=8,B=8,一個(gè)像素點(diǎn)占8+8+8+8=32位,4個(gè)字節(jié) RGB_565 表示16位RGB位圖,即R=5,G=6,B=5,它沒(méi)有透明度,一個(gè)像素點(diǎn)占5+6+5=16位,2個(gè)字節(jié)
我是用的小米手機(jī)2s來(lái)測(cè)試的,從sd卡取出一個(gè)照片,如下所示:
bit = BitmapFactory.decodeFile(Environment .getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/test.jpg"); Log.i("wechat", "壓縮前圖片的大小" + (bit.getByteCount() / 1024 / 1024) + "M寬度為" + bit.getWidth() + "高度為" + bit.getHeight());
將取得的bitmap進(jìn)行壓縮,下面開(kāi)始說(shuō),bitmap的幾種壓縮方式。
1.質(zhì)量壓縮
ByteArrayOutputStream baos = new ByteArrayOutputStream(); int quality = Integer.valueOf(editText.getText().toString()); bit.compress(CompressFormat.JPEG, quality, baos); byte[] bytes = baos.toByteArray(); bm = BitmapFactory.decodeByteArray(bytes, 0, bytes.length); Log.i("wechat", "壓縮后圖片的大小" + (bm.getByteCount() / 1024 / 1024) + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight() + "bytes.length= " + (bytes.length / 1024) + "KB" + "quality=" + quality);
可以看到,圖片的大小是沒(méi)有變的,因?yàn)橘|(zhì)量壓縮不會(huì)減少圖片的像素,它是在保持像素的前提下改變圖片的位深及透明度等,來(lái)達(dá)到壓縮圖片的目的,這也是為什么該方法叫質(zhì)量壓縮方法。那么,圖片的長(zhǎng),寬,像素都不變,那么bitmap所占內(nèi)存大小是不會(huì)變的。
但是我們看到bytes.length是隨著quality變小而變小的。這樣適合去傳遞二進(jìn)制的圖片數(shù)據(jù),比如微信分享圖片,要傳入二進(jìn)制數(shù)據(jù)過(guò)去,限制32kb之內(nèi)。
這里要說(shuō),如果是bit.compress(CompressFormat.PNG, quality, baos);這樣的png格式,quality就沒(méi)有作用了,bytes.length不會(huì)變化,因?yàn)閜ng圖片是無(wú)損的,不能進(jìn)行壓縮。
CompressFormat還有一個(gè)屬性是,CompressFormat.WEBP格式,該格式是google自己推出來(lái)一個(gè)圖片格式,更多信息,文末會(huì)貼出地址。
2.采樣率壓縮
BitmapFactory.Options options = new BitmapFactory.Options(); options.inSampleSize = 2; bm = BitmapFactory.decodeFile(Environment .getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/test.jpg", options); Log.i("wechat", "壓縮后圖片的大小" + (bm.getByteCount() / 1024 / 1024) + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
出來(lái)的log是
設(shè)置inSampleSize的值(int類型)后,假如設(shè)為2,則寬和高都為原來(lái)的1/2,寬高都減少了,自然內(nèi)存也降低了。
我上面的代碼沒(méi)用過(guò)options.inJustDecodeBounds = true; 因?yàn)槲沂枪潭▉?lái)取樣的數(shù)據(jù),為什么這個(gè)壓縮方法叫采樣率壓縮,是因?yàn)榕浜蟟nJustDecodeBounds,先獲取圖片的寬、高【這個(gè)過(guò)程就是取樣】,然后通過(guò)獲取的寬高,動(dòng)態(tài)的設(shè)置inSampleSize的值。
當(dāng)inJustDecodeBounds設(shè)置為true的時(shí)候,BitmapFactory通過(guò)decodeResource或者decodeFile解碼圖片時(shí),將會(huì)返回空(null)的Bitmap對(duì)象,這樣可以避免Bitmap的內(nèi)存分配,但是它可以返回Bitmap的寬度、高度以及MimeType。
3.縮放法壓縮(martix)
Matrix matrix = new Matrix(); matrix.setScale(0.5f, 0.5f); bm = Bitmap.createBitmap(bit, 0, 0, bit.getWidth(), bit.getHeight(), matrix, true); Log.i("wechat", "壓縮后圖片的大小" + (bm.getByteCount() / 1024 / 1024) + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
可以看出來(lái),bitmap的長(zhǎng)度和寬度分別縮小了一半,圖片大小縮小了四分之一。 關(guān)于martix更多信息,文末會(huì)有一個(gè)參考文章。
4.RGB_565法
BitmapFactory.Options options2 = new BitmapFactory.Options(); options2.inPreferredConfig = Bitmap.Config.RGB_565; bm = BitmapFactory.decodeFile(Environment .getExternalStorageDirectory().getAbsolutePath() + "/DCIM/Camera/test.jpg", options2); Log.i("wechat", "壓縮后圖片的大小" + (bm.getByteCount() / 1024 / 1024) + "M寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
出來(lái)的log是:
我們看到圖片大小直接縮小了一半,長(zhǎng)度和寬度也沒(méi)有變,相比argb_8888減少了一半的內(nèi)存。
注意:由于ARGB_4444的畫(huà)質(zhì)慘不忍睹,一般假如對(duì)圖片沒(méi)有透明度要求的話,可以改成RGB_565,相比ARGB_8888將節(jié)省一半的內(nèi)存開(kāi)銷(xiāo)。
5.createScaledBitmap
bm = Bitmap.createScaledBitmap(bit, 150, 150, true); Log.i("wechat", "壓縮后圖片的大小" + (bm.getByteCount() / 1024) + "KB寬度為" + bm.getWidth() + "高度為" + bm.getHeight());
這里是將圖片壓縮成用戶所期望的長(zhǎng)度和寬度,但是這里要說(shuō),如果用戶期望的長(zhǎng)度和寬度和原圖長(zhǎng)度寬度相差太多的話,圖片會(huì)很不清晰。
總結(jié)
以上就是5種圖片壓縮的方法,這里需要強(qiáng)調(diào),他們的壓縮僅僅只是對(duì)android中的bitmap來(lái)說(shuō)的。如果將這些壓縮后的bitmap另存為sd中,他們的內(nèi)存大小并不一樣。
android手機(jī)中,圖片的所占的內(nèi)存大小和很多因素相關(guān),計(jì)算起來(lái)也很麻煩。為了計(jì)算出一個(gè)圖片的內(nèi)存大小,可以將圖片當(dāng)做一個(gè)文件來(lái)間接計(jì)算,用如下的方法:
File file = new File(Environment.getExternalStorageDirectory() .getAbsolutePath() + "/DCIM/Camera/test.jpg"); Log.i("wechat", "file.length()=" + file.length() / 1024);
或者
FileInputStream fis = null; try { fis = new FileInputStream(file); } catch (FileNotFoundException e) { e.printStackTrace(); } try { Log.i("wechat", "fis.available()=" + fis.available() / 1024); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); }
上面兩個(gè)方法計(jì)算的結(jié)果是一樣的。
看完了這篇內(nèi)容,其實(shí)說(shuō)白了,Bitmap壓縮都是圍繞這個(gè)來(lái)做文章:Bitmap所占用的內(nèi)存 = 圖片長(zhǎng)度 x 圖片寬度 x 一個(gè)像素點(diǎn)占用的字節(jié)數(shù)。3個(gè)參數(shù),任意減少一個(gè)的值,就達(dá)到了壓縮的效果。
4.https://github.com/twohuoone/FunctionSet.git這個(gè)是我的github項(xiàng)目,里面還有我自己集成的一些小工具,里面都會(huì)有README,有興趣的可以看下
寫(xiě)完了,歡迎吐槽,本人比較喜歡研究,雖然很菜,有什么疑問(wèn)可以留評(píng)論,你們沒(méi)空我?guī)湍銈冄芯???