在Android开发中,当图片大小过大时,我常常所需要用到图片压缩,这里介绍图片压缩的两种常用场景:
- 从文件/流/byte[]中读取图片并压缩,返回Bitmap对象
- 将Bitmap压缩保存到本地文件中
一、Bitmap.Config
首先我想先说说这个Bitmap.Config这个枚举,它描述了图片一个像素所占得内存大小
- Bitmap.Config.ALPHA_8:没有颜色,只有透明度信息,每个像素点占8位(一个字节)
- Bitmap.Config.RGB_565:没有透明度信息,只有颜色信息,红色占5位、绿色占6位、蓝色占5位,即每个像素点占16位(两个字节)
- Bitmap.Config.ARGB_4444:既有颜色信息也有透明度信息,每个占4位共16位(两个字节),这个已经被标识为过时了,官方建议使用Bitmap.Config.ARGB_8888
- Bitmap.Config.ARGB_8888:既有颜色信息也有透明度信息,每个占8位共32位(四个字节),这是推荐使用的图片格式
二、读取图片压缩返回Bitmap
这里以读取文件为例,IO流和byte[]数组同理
/**
* 压缩图片至指定大小以内
* @param imagePath 图片路径
* @param maxWidth 指定最大宽度
* @param maxHeight 指定最大高度
* @return 返回压缩后的图片
*/
public static Bitmap compressImageFromFile(String imagePath, int maxWidth, int maxHeight) {
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;// 只读边,不读内容
BitmapFactory.decodeFile(imagePath, options);
int sampleSize = 1; // 默认压缩大小
int width = options.outWidth; // 图片原始宽度
int height = options.outHeight; // 图片原始高度
if (width < 0 || height < 0) { // 如果图片路径错误,返回null
return null;
}
while ((height / sampleSize > maxHeight) || (width / sampleSize > maxWidth)) {
sampleSize = sampleSize << 1; // sampleSize的值每次增长一倍
}
options.inJustDecodeBounds = false; // 不再只读边
options.inSampleSize = sampleSize;// 设置采样率大小
options.inPreferredConfig = Bitmap.Config.RGB_565; // 质量压缩,默认为Bitmap.Config.ARGB_8888
Bitmap bitmap = BitmapFactory.decodeFile(imagePath, options);
return bitmap;
}
这里先使用只读边的形式读取图片宽高信息,然后根据这个图片原始宽高和预设的最大宽大比较来计算出压缩比例sampleSize,设置质量压缩的操作可有可无,加上的话获取的图片大小将会更小,最后使用设置好的BitmapFactory.Options来获取压缩后的Bitmap并返回。
三、压缩Bitmap保存到本地
/**
* 压缩Bitmap并保存到本地,这里只支持保存为jpg格式的图片
* @param bitmap 要压缩的bitmap
* @param file 要保存的图片文件
*/
public static void compressBmpToFile(Bitmap bitmap, File file) {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int quality = 100; //压缩的质量,范围为0-100,100表示最高质量
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
Log.d("TAG", "baos.toByteArray().length : " + baos.toByteArray().length);
while (baos.toByteArray().length > 100 * 1024) {
baos.reset();
quality -= 10;
if (quality == 0) {
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
break;
}
bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
Log.d("TAG", "baos.toByteArray().length : " + baos.toByteArray().length);
}
try {
FileOutputStream fos = new FileOutputStream(file);
fos.write(baos.toByteArray());
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
这里主要是通过Bitmap.compress(BItmap.CompressFormat format, int quality, OutputStream stream)这个方法进行压缩保存,其中qualite就是保存图片的质量,越高代表质量越高。
我们可以看下打印的log信息,这里我使用了一张 312KB 的图片测试
07-30 15:17:12.681 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 3536745
07-30 15:17:12.884 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 1071551
07-30 15:17:13.060 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 745503
07-30 15:17:13.254 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 606260
07-30 15:17:13.419 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 520280
07-30 15:17:13.577 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 465814
07-30 15:17:13.736 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 415198
07-30 15:17:13.888 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 362973
07-30 15:17:14.052 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 301712
07-30 15:17:14.204 11318-11318/com.example.msi_cn.myapplication D/TAG: baos.toByteArray().length : 228611
07-30 15:17:14.356 11318-11318/com.example.msi_cn.myapplication D/TAG: file path : /data/user/0/com.example.msi_cn.myapplication/cache/test.jpg
07-30 15:17:14.356 11318-11318/com.example.msi_cn.myapplication D/TAG: file size : 153045
调用代码:
Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.test);
File file = new File(getCacheDir(), "test.jpg");
BitmapUtil.compressBmpToFile(bitmap , file);
Log.d("TAG", "file path : " + file.getAbsolutePath());
try {
FileInputStream fis = new FileInputStream(file);
Log.d("TAG", "file size : " + fis.available());
} catch (IOException e) {
e.printStackTrace();
}
当然,一般我们使用的话,可以自己把这个质量的大小写死减少计算,即
/**
* 压缩Bitmap并保存到本地,这里只支持保存为jpg格式的图片
* @param bitmap 要压缩的bitmap
* @param file 要保存的图片文件
*/
public static void compressBmpToFile(Bitmap bitmap, File file) {
try {
FileOutputStream fos = new FileOutputStream(file);
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
fos.flush();
fos.close();
} catch (Exception e) {
e.printStackTrace();
}
}
是不是一下子简洁了很多呢