複習一下圖片的壓縮知識,今天來做一個總結。 參考:https://blog.csdn.net/baidu_38477614/article/details/78901107
理論概括
1.圖片存在的幾種形式:
- File,存在於我們的磁盤中,我們通常說的圖片大小。
- Stream即流的形式,比如我們上傳網絡圖片。
- Bitmap,就是我們通常指內存中圖片的大小。
2. 什麼是質量壓縮?
圖片的質量壓縮,會改變圖片在磁盤中的大小(File文件的大小),不能改變圖片在加載時,在內存中的大小。 原理是:通過算法扣掉(同化)了 圖片中的一些某個點附近相近的像素,達到降低質量 減少 文件大小的目的。 應用場景:圖片的上傳。
3.什麼是尺寸壓縮?
圖片的尺寸壓縮是指:按照一定的倍數對圖片減少單位尺寸的像素值,可以改變圖片在內存中的大小,不改變圖片在磁盤中的大小。 原理是:通過減少單位尺寸的像素值,真正意義上的降低像素值。 應用場景:用戶頭像的縮略圖。
實戰
我們的界面也很簡單,就是兩個按鈕,分別是拍照和相冊選擇,一個ImageView,用來顯示壓縮後的圖片,如圖:
image.png
由於我們這裏只講圖片的壓縮,關於再次之前如何獲取圖片返回的URI和高低版本適配7.0等問題,我們這裏不說,我之前寫過文章,Android-圖片的選擇,裁剪,壓縮,適配高版本,這裏就不說了。 我們直接從圖片獲取到拍照或者相冊返回的URI開始說起,上圖:
image.png
質量壓縮
那我們就先看bitmapCompress()這個質量壓縮的方法。
/** * 這裏我們生成了一個Pic文件夾,在下面放了我們質量壓縮後的圖片,用於和原圖對比 * 壓縮圖片使用Bitmap.compress(),這裏是質量壓縮 */ public void bitmapCompress(Uri uriClipUri) { try { //裁剪後的圖像轉成BitMap //photoBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uriClipUri)); photoBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(), uriClipUri); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //創建路徑 String path = Environment.getExternalStorageDirectory() .getPath() + "/Pic"; //獲取外部儲存目錄 file = new File(path); //創建新目錄, 創建此抽象路徑名指定的目錄,包括創建必需但不存在的父目錄。 file.mkdirs(); //以當前時間重新命名文件 long i = System.currentTimeMillis(); //生成新的文件 file = new File(file.toString() + "/" + i + ".png"); Log.e("fileNew", file.getPath()); //創建輸出流 OutputStream out = null; try { out = new FileOutputStream(file.getPath()); } catch (FileNotFoundException e) { e.printStackTrace(); } //壓縮文件,返回結果,參數分別是壓縮的格式,壓縮質量的百分比,輸出流 boolean bCompress = photoBitmap.compress(Bitmap.CompressFormat.JPEG, 50, out); try { photoBitmap = MediaStore.Images.Media.getBitmap(getContentResolver(),Uri.fromFile(file)); } catch (IOException e) { e.printStackTrace(); } imageView.setImageBitmap(photoBitmap); }
我們看一眼結果: 結果是Imageview沒有正常加載圖片,怎麼回事?難道圖片沒有生成,文件創建失敗? 我們看一眼原圖片和壓縮目錄(Pic)下有沒有文件: 原文件:
壓縮後的文件:
可以看到原文件和壓縮後的文件都生成了,而且也從6.61M壓縮爲了1.52M,那爲什麼圖片不正常顯示呢?,在看一眼日誌:
image.png
大家明白了吧,這個結果也和我們之前說的質量壓縮只是改變磁盤中的文件大小,並不能改變加載時內存中的圖片大小
尺寸壓縮
尺寸壓縮的方法:
Bitmap photoBitmap; File file; /** * 壓縮圖片使用,採用BitmapFactory.decodeFile。這裏是尺寸壓縮 */ public void bitmapFactory(Uri imageUri){ String[] filePathColumns = {MediaStore.Images.Media.DATA}; Cursor c = getContentResolver().query(imageUri, filePathColumns, null, null, null); c.moveToFirst(); int columnIndex = c.getColumnIndex(filePathColumns[0]); String imagePath = c.getString(columnIndex); c.close(); // 配置壓縮的參數 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; //獲取當前圖片的邊界大小,而不是將整張圖片載入在內存中,避免內存溢出 BitmapFactory.decodeFile(imagePath, options); options.inJustDecodeBounds = false; ////inSampleSize的作用就是可以把圖片的長短縮小inSampleSize倍,所佔內存縮小inSampleSize的平方 options.inSampleSize = caculateSampleSize(options,500,50); Bitmap bm = BitmapFactory.decodeFile(imagePath, options); // 解碼文件 imageView.setImageBitmap(bm); } /** * 計算出所需要壓縮的大小 * @param options * @param reqWidth 我們期望的圖片的寬,單位px * @param reqHeight 我們期望的圖片的高,單位px * @return */ private int caculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { int sampleSize = 1; int picWidth = options.outWidth; int picHeight = options.outHeight; if (picWidth > reqWidth || picHeight > reqHeight) { int halfPicWidth = picWidth / 2; int halfPicHeight = picHeight / 2; while (halfPicWidth / sampleSize > reqWidth || halfPicHeight / sampleSize > reqHeight) { sampleSize *= 2; } } return sampleSize; }
結果:
圖片正常顯示,磁盤中圖片的大小並沒有改變,只是改變了加載時內存中的圖片大小。
補充
- 質量壓縮無法避免oom,但可以改變圖片在磁盤中或者說是File文件的大小,尺寸壓縮可以避免OOM,但不改變圖片本身的大小,只改變加載是在內存中的大小,即bitmap.
- 質量壓縮我們的主要方法是:MediaStore.Images.Media.getBitmap或者BitmapFactory.decodeStream;尺寸壓縮我們用到的方法是:BitmapFactory.decodeFile
主要就說完了,我們在實際運用中可以把這兩個方法作爲工具類,隨時調用。 demo上傳github,地址:圖片的質量和尺寸壓縮