在網上調查了圖片壓縮的方法並實裝後,大致上可以認爲有兩類壓縮:質量壓縮(不改變圖片的尺寸)和尺寸壓縮(相當於是像素上的壓縮);質量壓縮一般可用於上傳大圖前的處理,這樣就可以節省一定的流量,畢竟現在的手機拍照都能達到3M左右了,尺寸壓縮一般可用於生成縮略圖。
兩種方法都實裝在了我的項目中,結果卻發現在質量壓縮的模塊中,本來1.9M的圖片壓縮後反而變成3M多了,很是奇怪,再做了進一步調查終於知道原因了。下面這個博客說的比較清晰:
android圖片壓縮總結
總結來看,圖片有三種存在形式:硬盤上時是file,網絡傳輸時是stream,內存中是stream或bitmap,所謂的質量壓縮,它其實只能實現對file的影響,你可以把一個file轉成bitmap再轉成file,或者直接將一個bitmap轉成file時,這個最終的file是被壓縮過的,但是中間的bitmap並沒有被壓縮(或者說幾乎沒有被壓縮,我不確定),因爲bigmap在內存中的大小是按像素計算的,也就是width * height,對於質量壓縮,並不會改變圖片的像素,所以就算質量被壓縮了,但是bitmap在內存的佔有率還是沒變小,但你做成file時,它確實變小了;
而尺寸壓縮由於是減小了圖片的像素,所以它直接對bitmap產生了影響,當然最終的file也是相對的變小了;
最後把自己總結的工具類貼出來:
二、按比例大小壓縮 (路徑獲取圖片)
兩種方法都實裝在了我的項目中,結果卻發現在質量壓縮的模塊中,本來1.9M的圖片壓縮後反而變成3M多了,很是奇怪,再做了進一步調查終於知道原因了。下面這個博客說的比較清晰:
android圖片壓縮總結
總結來看,圖片有三種存在形式:硬盤上時是file,網絡傳輸時是stream,內存中是stream或bitmap,所謂的質量壓縮,它其實只能實現對file的影響,你可以把一個file轉成bitmap再轉成file,或者直接將一個bitmap轉成file時,這個最終的file是被壓縮過的,但是中間的bitmap並沒有被壓縮(或者說幾乎沒有被壓縮,我不確定),因爲bigmap在內存中的大小是按像素計算的,也就是width * height,對於質量壓縮,並不會改變圖片的像素,所以就算質量被壓縮了,但是bitmap在內存的佔有率還是沒變小,但你做成file時,它確實變小了;
而尺寸壓縮由於是減小了圖片的像素,所以它直接對bitmap產生了影響,當然最終的file也是相對的變小了;
最後把自己總結的工具類貼出來:
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.File;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
- import android.graphics.Bitmap;
- import android.graphics.Bitmap.Config;
- import android.graphics.BitmapFactory;
- /**
- * Image compress factory class
- *
- * @author
- *
- */
- public class ImageFactory {
- /**
- * Get bitmap from specified image path
- *
- * @param imgPath
- * @return
- */
- public Bitmap getBitmap(String imgPath) {
- // Get bitmap through image path
- BitmapFactory.Options newOpts = new BitmapFactory.Options();
- newOpts.inJustDecodeBounds = false;
- newOpts.inPurgeable = true;
- newOpts.inInputShareable = true;
- // Do not compress
- newOpts.inSampleSize = 1;
- newOpts.inPreferredConfig = Config.RGB_565;
- return BitmapFactory.decodeFile(imgPath, newOpts);
- }
- /**
- * Store bitmap into specified image path
- *
- * @param bitmap
- * @param outPath
- * @throws FileNotFoundException
- */
- public void storeImage(Bitmap bitmap, String outPath) throws FileNotFoundException {
- FileOutputStream os = new FileOutputStream(outPath);
- bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);
- }
- /**
- * Compress image by pixel, this will modify image width/height.
- * Used to get thumbnail
- *
- * @param imgPath image path
- * @param pixelW target pixel of width
- * @param pixelH target pixel of height
- * @return
- */
- public Bitmap ratio(String imgPath, float pixelW, float pixelH) {
- BitmapFactory.Options newOpts = new BitmapFactory.Options();
- // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true,即只讀邊不讀內容
- newOpts.inJustDecodeBounds = true;
- newOpts.inPreferredConfig = Config.RGB_565;
- // Get bitmap info, but notice that bitmap is null now
- Bitmap bitmap = BitmapFactory.decodeFile(imgPath,newOpts);
- newOpts.inJustDecodeBounds = false;
- int w = newOpts.outWidth;
- int h = newOpts.outHeight;
- // 想要縮放的目標尺寸
- float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了
- float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了
- // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
- int be = 1;//be=1表示不縮放
- if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放
- be = (int) (newOpts.outWidth / ww);
- } else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放
- be = (int) (newOpts.outHeight / hh);
- }
- if (be <= 0) be = 1;
- newOpts.inSampleSize = be;//設置縮放比例
- // 開始壓縮圖片,注意此時已經把options.inJustDecodeBounds 設回false了
- bitmap = BitmapFactory.decodeFile(imgPath, newOpts);
- // 壓縮好比例大小後再進行質量壓縮
- // return compress(bitmap, maxSize); // 這裏再進行質量壓縮的意義不大,反而耗資源,刪除
- return bitmap;
- }
- /**
- * Compress image by size, this will modify image width/height.
- * Used to get thumbnail
- *
- * @param image
- * @param pixelW target pixel of width
- * @param pixelH target pixel of height
- * @return
- */
- public Bitmap ratio(Bitmap image, float pixelW, float pixelH) {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- image.compress(Bitmap.CompressFormat.JPEG, 100, os);
- if( os.toByteArray().length / 1024>1024) {//判斷如果圖片大於1M,進行壓縮避免在生成圖片(BitmapFactory.decodeStream)時溢出
- os.reset();//重置baos即清空baos
- image.compress(Bitmap.CompressFormat.JPEG, 50, os);//這裏壓縮50%,把壓縮後的數據存放到baos中
- }
- ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());
- BitmapFactory.Options newOpts = new BitmapFactory.Options();
- //開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
- newOpts.inJustDecodeBounds = true;
- newOpts.inPreferredConfig = Config.RGB_565;
- Bitmap bitmap = BitmapFactory.decodeStream(is, null, newOpts);
- newOpts.inJustDecodeBounds = false;
- int w = newOpts.outWidth;
- int h = newOpts.outHeight;
- float hh = pixelH;// 設置高度爲240f時,可以明顯看到圖片縮小了
- float ww = pixelW;// 設置寬度爲120f,可以明顯看到圖片縮小了
- //縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
- int be = 1;//be=1表示不縮放
- if (w > h && w > ww) {//如果寬度大的話根據寬度固定大小縮放
- be = (int) (newOpts.outWidth / ww);
- } else if (w < h && h > hh) {//如果高度高的話根據寬度固定大小縮放
- be = (int) (newOpts.outHeight / hh);
- }
- if (be <= 0) be = 1;
- newOpts.inSampleSize = be;//設置縮放比例
- //重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
- is = new ByteArrayInputStream(os.toByteArray());
- bitmap = BitmapFactory.decodeStream(is, null, newOpts);
- //壓縮好比例大小後再進行質量壓縮
- // return compress(bitmap, maxSize); // 這裏再進行質量壓縮的意義不大,反而耗資源,刪除
- return bitmap;
- }
- /**
- * Compress by quality, and generate image to the path specified
- *
- * @param image
- * @param outPath
- * @param maxSize target will be compressed to be smaller than this size.(kb)
- * @throws IOException
- */
- public void compressAndGenImage(Bitmap image, String outPath, int maxSize) throws IOException {
- ByteArrayOutputStream os = new ByteArrayOutputStream();
- // scale
- int options = 100;
- // Store the bitmap into output stream(no compress)
- image.compress(Bitmap.CompressFormat.JPEG, options, os);
- // Compress by loop
- while ( os.toByteArray().length / 1024 > maxSize) {
- // Clean up os
- os.reset();
- // interval 10
- options -= 10;
- image.compress(Bitmap.CompressFormat.JPEG, options, os);
- }
- // Generate compressed image file
- FileOutputStream fos = new FileOutputStream(outPath);
- fos.write(os.toByteArray());
- fos.flush();
- fos.close();
- }
- /**
- * Compress by quality, and generate image to the path specified
- *
- * @param imgPath
- * @param outPath
- * @param maxSize target will be compressed to be smaller than this size.(kb)
- * @param needsDelete Whether delete original file after compress
- * @throws IOException
- */
- public void compressAndGenImage(String imgPath, String outPath, int maxSize, boolean needsDelete) throws IOException {
- compressAndGenImage(getBitmap(imgPath), outPath, maxSize);
- // Delete original file
- if (needsDelete) {
- File file = new File (imgPath);
- if (file.exists()) {
- file.delete();
- }
- }
- }
- /**
- * Ratio and generate thumb to the path specified
- *
- * @param image
- * @param outPath
- * @param pixelW target pixel of width
- * @param pixelH target pixel of height
- * @throws FileNotFoundException
- */
- public void ratioAndGenThumb(Bitmap image, String outPath, float pixelW, float pixelH) throws FileNotFoundException {
- Bitmap bitmap = ratio(image, pixelW, pixelH);
- storeImage( bitmap, outPath);
- }
- /**
- * Ratio and generate thumb to the path specified
- *
- * @param image
- * @param outPath
- * @param pixelW target pixel of width
- * @param pixelH target pixel of height
- * @param needsDelete Whether delete original file after compress
- * @throws FileNotFoundException
- */
- public void ratioAndGenThumb(String imgPath, String outPath, float pixelW, float pixelH, boolean needsDelete) throws FileNotFoundException {
- Bitmap bitmap = ratio(imgPath, pixelW, pixelH);
- storeImage( bitmap, outPath);
- // Delete original file
- if (needsDelete) {
- File file = new File (imgPath);
- if (file.exists()) {
- file.delete();
- }
- }
- }
- }
如果上面的工具類不滿足你,那麼看看下面的方法。
一、圖片質量壓縮
- /**
- * 質量壓縮方法
- *
- * @param image
- * @return
- */
- public static Bitmap compressImage(Bitmap image) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- image.compress(Bitmap.CompressFormat.JPEG, 100, baos);// 質量壓縮方法,這裏100表示不壓縮,把壓縮後的數據存放到baos中
- int options = 90;
- while (baos.toByteArray().length / 1024 > 100) { // 循環判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮
- baos.reset(); // 重置baos即清空baos
- image.compress(Bitmap.CompressFormat.JPEG, options, baos);// 這裏壓縮options%,把壓縮後的數據存放到baos中
- options -= 10;// 每次都減少10
- }
- ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());// 把壓縮後的數據baos存放到ByteArrayInputStream中
- Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);// 把ByteArrayInputStream數據生成圖片
- return bitmap;
- }
二、按比例大小壓縮 (路徑獲取圖片)
- /**
- * 圖片按比例大小壓縮方法
- *
- * @param srcPath (根據路徑獲取圖片並壓縮)
- * @return
- */
- public static Bitmap getimage(String srcPath) {
- BitmapFactory.Options newOpts = new BitmapFactory.Options();
- // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
- newOpts.inJustDecodeBounds = true;
- Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);// 此時返回bm爲空
- newOpts.inJustDecodeBounds = false;
- int w = newOpts.outWidth;
- int h = newOpts.outHeight;
- // 現在主流手機比較多是800*480分辨率,所以高和寬我們設置爲
- float hh = 800f;// 這裏設置高度爲800f
- float ww = 480f;// 這裏設置寬度爲480f
- // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
- int be = 1;// be=1表示不縮放
- if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放
- be = (int) (newOpts.outWidth / ww);
- } else if (w < h && h > hh) {// 如果高度高的話根據寬度固定大小縮放
- be = (int) (newOpts.outHeight / hh);
- }
- if (be <= 0)
- be = 1;
- newOpts.inSampleSize = be;// 設置縮放比例
- // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
- bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
- return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮
- }
- /**
- * 圖片按比例大小壓縮方法
- *
- * @param image (根據Bitmap圖片壓縮)
- * @return
- */
- public static Bitmap compressScale(Bitmap image) {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();
- image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
- // 判斷如果圖片大於1M,進行壓縮避免在生成圖片(BitmapFactory.decodeStream)時溢出
- if (baos.toByteArray().length / 1024 > 1024) {
- baos.reset();// 重置baos即清空baos
- image.compress(Bitmap.CompressFormat.JPEG, 80, baos);// 這裏壓縮50%,把壓縮後的數據存放到baos中
- }
- ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
- BitmapFactory.Options newOpts = new BitmapFactory.Options();
- // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
- newOpts.inJustDecodeBounds = true;
- Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
- newOpts.inJustDecodeBounds = false;
- int w = newOpts.outWidth;
- int h = newOpts.outHeight;
- Log.i(TAG, w + "---------------" + h);
- // 現在主流手機比較多是800*480分辨率,所以高和寬我們設置爲
- // float hh = 800f;// 這裏設置高度爲800f
- // float ww = 480f;// 這裏設置寬度爲480f
- float hh = 512f;
- float ww = 512f;
- // 縮放比。由於是固定比例縮放,只用高或者寬其中一個數據進行計算即可
- int be = 1;// be=1表示不縮放
- if (w > h && w > ww) {// 如果寬度大的話根據寬度固定大小縮放
- be = (int) (newOpts.outWidth / ww);
- } else if (w < h && h > hh) { // 如果高度高的話根據高度固定大小縮放
- be = (int) (newOpts.outHeight / hh);
- }
- if (be <= 0)
- be = 1;
- newOpts.inSampleSize = be; // 設置縮放比例
- // newOpts.inPreferredConfig = Config.RGB_565;//降低圖片從ARGB888到RGB565
- // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
- isBm = new ByteArrayInputStream(baos.toByteArray());
- bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);
- return compressImage(bitmap);// 壓縮好比例大小後再進行質量壓縮
- //return bitmap;
- }
--------------------------------------------------------------------------------------------------------------------------------
分享個按照圖片尺寸壓縮:
- public static void compressPicture(String srcPath, String desPath) {
- FileOutputStream fos = null;
- BitmapFactory.Options op = new BitmapFactory.Options();
- // 開始讀入圖片,此時把options.inJustDecodeBounds 設回true了
- op.inJustDecodeBounds = true;
- Bitmap bitmap = BitmapFactory.decodeFile(srcPath, op);
- op.inJustDecodeBounds = false;
- // 縮放圖片的尺寸
- float w = op.outWidth;
- float h = op.outHeight;
- float hh = 1024f;//
- float ww = 1024f;//
- // 最長寬度或高度1024
- float be = 1.0f;
- if (w > h && w > ww) {
- be = (float) (w / ww);
- } else if (w < h && h > hh) {
- be = (float) (h / hh);
- }
- if (be <= 0) {
- be = 1.0f;
- }
- op.inSampleSize = (int) be;// 設置縮放比例,這個數字越大,圖片大小越小.
- // 重新讀入圖片,注意此時已經把options.inJustDecodeBounds 設回false了
- bitmap = BitmapFactory.decodeFile(srcPath, op);
- int desWidth = (int) (w / be);
- int desHeight = (int) (h / be);
- bitmap = Bitmap.createScaledBitmap(bitmap, desWidth, desHeight, true);
- try {
- fos = new FileOutputStream(desPath);
- if (bitmap != null) {
- bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
- }
- } catch (FileNotFoundException e) {
- e.printStackTrace();
- }
- }
一、調用getDrawingCache()前先要測量,否則的話得到的bitmap爲null,這個我在OnCreate()、OnStart()、OnResume()方法裏都試驗過。
二、當調用bitmap.compress(CompressFormat.JPEG, 100, fos);保存爲圖片時發現圖片背景爲黑色,如下圖:
這時只需要改成用png保存就可以了,bitmap.compress(CompressFormat.PNG, 100, fos);,如下圖:
在實際開發中,有時候我們需求將文件轉換爲字符串,然後作爲參數進行上傳。
必備工具類圖片bitmap轉成字符串string與String字符串轉換爲bitmap圖片格式
- import android.graphics.Bitmap;
- import android.graphics.BitmapFactory;
- import android.util.Base64;
- import java.io.ByteArrayOutputStream;
- /**
- *
- *
- * 功能描述:Android開發之常用必備工具類圖片bitmap轉成字符串string與String字符串轉換爲bitmap圖片格式
- */
- public class BitmapAndStringUtils {
- /**
- * 圖片轉成string
- *
- * @param bitmap
- * @return
- */
- public static String convertIconToString(Bitmap bitmap)
- {
- ByteArrayOutputStream baos = new ByteArrayOutputStream();// outputstream
- bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
- byte[] appicon = baos.toByteArray();// 轉爲byte數組
- return Base64.encodeToString(appicon, Base64.DEFAULT);
- }
- /**
- * string轉成bitmap
- *
- * @param st
- */
- public static Bitmap convertStringToIcon(String st)
- {
- // OutputStream out;
- Bitmap bitmap = null;
- try
- {
- // out = new FileOutputStream("/sdcard/aa.jpg");
- byte[] bitmapArray;
- bitmapArray = Base64.decode(st, Base64.DEFAULT);
- bitmap =
- BitmapFactory.decodeByteArray(bitmapArray, 0,
- bitmapArray.length);
- // bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
- return bitmap;
- }
- catch (Exception e)
- {
- return null;
- }
- }
- }
如果你的圖片是File文件,可以用下面代碼:
- /**
- * 圖片文件轉換爲指定編碼的字符串
- *
- * @param imgFile 圖片文件
- */
- public static String file2String(File imgFile) {
- InputStream in = null;
- byte[] data = null;
- //讀取圖片字節數組
- try{
- in = new FileInputStream(imgFile);
- data = new byte[in.available()];
- in.read(data);
- in.close();
- } catch (IOException e){
- e.printStackTrace();
- }
- //對字節數組Base64編碼
- BASE64Encoder encoder = new BASE64Encoder();
- String result = encoder.encode(data);
- return result;//返回Base64編碼過的字節數組字符串
- }