好长时间没写博客了,最近忙着毕业的事。其实我一直想写一篇关于图片压缩的博客,写博客其实也是为了整理一下自己学过的知识和思路作为记录。
Android的图片压缩不仅是在移动开发中是难点,在任何语言中处理起来都不是特别简单。一般来说分为两种:尺寸压缩和质量压缩,这两种是最常见的,在APP开发过程中图片上传最是很常见的了,现在市面上的手机像素都非常高,比如Vivo手机一张图片差不多得15M左右,这对于用户来说就太痛苦了,上传一张图片,这个月的流量就没了,咱们不能这么干,得把图片压缩到一定的程度再上传比较贴切。下面提供两种压缩图片的方式:
一、尺寸压缩的方法一(根据图片的路径进行压缩):
/** * 图片按比例大小压缩方法(根据路径获取图片并压缩) * * @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);// 此时返回Bitmap为空 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); }二、尺寸压缩的方法二(根据图片的Bitmap进行压缩):
/** * 图片按比例大小压缩方法(根据Bitmap图片压缩) * * @param image * @return */ public static Bitmap comp(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); // 判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出 if (baos.toByteArray().length / 1024 > 1024) { // 重置BAOS即清空BAOS baos.reset(); // 这里压缩50%,把压缩后的数据存放到BAOS中 image.compress(Bitmap.CompressFormat.JPEG, 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; // 现在主流手机比较多是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了 isBm = new ByteArrayInputStream(baos.toByteArray()); bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); // 压缩好比例大小后再进行质量压缩 return compressImage(bitmap); }但是有的时候单纯的使用尺寸压缩并不能压缩到你自己需要的内存大小,而且如果用尺寸压缩力度太大,会导致图片失真, 可以看到上面的代码进行尺寸压缩之后随之又调用了一个方法compressImage(bitmap);这个方法是质量压缩,将尺寸压缩和质量压缩结合使用,这样的优点是图片压缩之后不会失真。
三、图片的质量压缩(抽取部分像素)
/** * 图片的质量压缩 * * @param image * @return */ public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到BAOS中 image.compress(Bitmap.CompressFormat.JPEG, 100, baos); int options = 100; // 循环判断如果压缩后图片是否大于100kb,大于继续压缩 while (baos.toByteArray().length / 1024 > 100) { // 重置BAOS即清空BAOS baos.reset(); // 这里压缩options%,把压缩后的数据存放到BAOS中 image.compress(Bitmap.CompressFormat.JPEG, options, baos); // 每次都减少10 options -= 10; } // 把压缩后的数据BAOS存放到ByteArrayInputStream中 ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); // 把ByteArrayInputStream数据生成图片 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); return bitmap; }下面是我所用到的图片处理的工具类,大家可以copy直接使用:
package com.reduce_weight.utils; 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 java.util.Date; import android.annotation.SuppressLint; import android.graphics.Bitmap; import android.graphics.BitmapFactory; public class PictureUtil { /** * 将bitmap位图保存到path路径下 * * @param bitmap */ @SuppressLint("SdCardPath") public static String saveBitmap(Bitmap bitmap) { File filePath = new File("/sdcard/图片缓存/"); if (!filePath.exists()) { filePath.mkdir(); } File file = new File("/sdcard/图片缓存/", new Date().getTime() + "temp.png"); // 创建文件 if (file.exists()) { file.delete(); } try { FileOutputStream out = new FileOutputStream(file); bitmap.compress(Bitmap.CompressFormat.PNG, 90, out); out.flush(); out.close(); } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return file.getAbsolutePath(); } /** * 图片按比例大小压缩方法(根据路径获取图片并压缩) * * @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);// 此时返回Bitmap为空 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); } /** * 图片按比例大小压缩方法(根据Bitmap图片压缩) * * @param image * @return */ public static Bitmap comp(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos); // 判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出 if (baos.toByteArray().length / 1024 > 1024) { // 重置BAOS即清空BAOS baos.reset(); // 这里压缩50%,把压缩后的数据存放到BAOS中 image.compress(Bitmap.CompressFormat.JPEG, 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; // 现在主流手机比较多是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了 isBm = new ByteArrayInputStream(baos.toByteArray()); bitmap = BitmapFactory.decodeStream(isBm, null, newOpts); // 压缩好比例大小后再进行质量压缩 return compressImage(bitmap); } /** * 图片的质量压缩 * * @param image * @return */ public static Bitmap compressImage(Bitmap image) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); // 质量压缩方法,这里100表示不压缩,把压缩后的数据存放到BAOS中 image.compress(Bitmap.CompressFormat.JPEG, 100, baos); int options = 100; // 循环判断如果压缩后图片是否大于100kb,大于继续压缩 while (baos.toByteArray().length / 1024 > 100) { // 重置BAOS即清空BAOS baos.reset(); // 这里压缩options%,把压缩后的数据存放到BAOS中 image.compress(Bitmap.CompressFormat.JPEG, options, baos); // 每次都减少10 options -= 10; } // 把压缩后的数据BAOS存放到ByteArrayInputStream中 ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray()); // 把ByteArrayInputStream数据生成图片 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null); return bitmap; } }本人在项目中用的就是这种方式进行的压缩,上传到服务器都在100K以下,而且图片打开还是比较清楚的,感觉这种压缩方式还是比较靠谱的,大家如果有好的方式可以直接留言告诉我,我们一起共同学习。