项目中用到了图片上传,拍照和直接上传本地图片,但现在的手机普遍拍出来的照片都很大,基本都是3M左右,上传这么大的图片费流量不说,上传时间也很久,肯定是必须要压缩的。
1.质量压缩Bitmap
Bitmap
类有个compress()
方法,可以将bitmap图片压缩到指定质量。
public boolean compress (Bitmap.CompressFormat format, int quality, OutputStream stream)
quality
——是压缩质量,值越大,压缩后图片的大小越大。
CompressFormat
——有三个值可选,JPEG,PNG和WEBP
,需要注意的是,如果选用PNG格式的话,quality是无效的
quality的解释:
Hint to the compressor, 0-100. 0 meaning compress for small size, 100 meaning compress for max quality. Some formats, like PNG which is lossless, will ignore the quality setting
代码:
假设sd卡上MyPic
目录下都是图片,取第一张来压缩
注意:bitmap在内存中大小和在sd卡中大小是不同的。
//质量压缩(宽,高不变,即像素不变,压缩的是透明度和位深)
File folder = new File(Environment.getExternalStorageDirectory().getAbsolutePath()
+ "/MyPic/");
File[] files = folder.listFiles();
File file = files[0];//获取第一张图片
double sdSize = file.length() / 1024;//sd卡中的大小,直接文件的大小
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
int cacheSize = bitmap.getByteCount() / 1024;//内存中大小
Log.i("test", "压缩前图片内存中大小是: " + cacheSize + "K, SD卡中大小是: " + sdSize
+ "K 宽度是: " + bitmap.getWidth() + " 高度是: " + bitmap.getHeight());
结果:
压缩前图片内存中大小是: 2840K, SD卡中大小是: 57.0K 宽度是: 640 高度是: 1136
压缩图片并保存
String newPath = Environment.getExternalStorageDirectory().getAbsolutePath() +
"/MyPic/test_compressed.jpg";
try {
//文件输出流,直接压缩后保存到指定路径
FileOutputStream fos = new FileOutputStream(newPath);
//quality越大,压缩后的大小越大,取值0-100
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, fos);
fos.flush();
fos.close();
bitmap.recycle();
sdSize = new File(newPath).length() / 1024;
bitmap = BitmapFactory.decodeFile(newPath);
cacheSize = bitmap.getByteCount() / 1024;
Log.i("test", "压缩前图片内存中大小是: " + cacheSize + "K, SD卡中大小是: " + sdSize
+ "K 宽度是: " + bitmap.getWidth() + " 高度是: " + bitmap.getHeight());
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
还可以用二进制输出流,如果上传图片分享图片是二进制流的情况。
ByteArrayOutputStream baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 50, baos);
byte[] bytes = baos.toByteArray();
bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
结果:
压缩前图片内存中大小是: 2840K, SD卡中大小是: 57.0K 宽度是: 640 高度是: 1136
压缩后图片内存中大小是: 2840K, SD卡中大小是: 24.0K 宽度是: 640 高度是: 1136
别忘了添加读写sd卡的权限:
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
可以看到结果,大小被压缩了,但图片宽高即像素并没有改变,图片占的内存与像素有关,所以也并没有改变。
2.宽高压缩BitmapFactory.Options
options.inSampleSize
表示的是压缩比率。值大于1的时候才有效,表示宽高的缩短比例,小于1的时候默认按照1来。最终的取值都是按照2的倍数来,最好设置成2的倍数。
代码:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = 2;//宽高压缩比例
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
try {
FileOutputStream fos = new FileOutputStream(newPath);
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);//同比例保存到文件
File myPic = new File(newPath);
sdSize = myPic.length() / 1024;
cacheSize = bitmap.getByteCount() / 2014;
Log.i("test", "压缩后图片内存中大小是: " + cacheSize + "K, SD卡中大小是: " + sdSize
+ "K 宽度是: " + bitmap.getWidth() + " 高度是: " + bitmap.getHeight());
} catch (FileNotFoundException e) {
e.printStackTrace();
}
结果:
压缩前图片内存中大小是: 2840K, SD卡中大小是: 57.0K 宽度是: 640 高度是: 1136
压缩后图片内存中大小是: 360K, SD卡中大小是: 41.0K 宽度是: 320 高度是: 568
可以看到宽高,都缩短到了原来1/2,大小和内存中大小都改变了。
options.inSampleSize
这个值一般需要动态获取,此时还需要inJustDecodeBounds
这个属性。
为true
的时候并不返回bitmap本身,但可以获取宽高,可以用来计算inSampleSize
,以免oom,之后再设置为false
,即可再压缩图片。
代码:
Bitmap bitmap = BitmapFactory.decodeFile(file.getAbsolutePath());
Log.i("test", "压缩前图片内存中大小是: " + bitmap.getByteCount()
+ "K 宽度是: " + bitmap.getWidth() + " 高度是: " + bitmap.getHeight());
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//设置为true时,并不返回bitmap,不用担心oom
BitmapFactory.decodeFile(file.getAbsolutePath(), options);//执行完后已经获得原图的宽和高
//计算出需要缩放的比例,按照传入的宽高
options.inSampleSize = getSampliSize(options, 400, 800);
options.inJustDecodeBounds = false;//设置为false,返回bitmap,要开始压缩了
bitmap = BitmapFactory.decodeFile(file.getAbsolutePath(), options);
Log.i("test", "压缩后图片内存中大小是: " + bitmap.getByteCount()
+ "K 宽度是: " + bitmap.getWidth() + " 高度是: " + bitmap.getHeight());
getSampliSize()方法代码:
private int getSampliSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
//获取真实宽和高来计算需要的inSampleSize值
int width = options.outWidth;
int height = options.outHeight;
//调整到高比宽大的模式,调整都相同的模式比较
if (height < width) {
int temp = height;
height = width;
width = temp;
}
if (reqHeight < reqWidth) {
int temp = reqHeight;
reqHeight = reqWidth;
reqWidth = temp;
}
int inSampleSize = 1;
// 只要有一个缩放到给定的值后就停止,即此时缩放的比例是最接近给定的大小的
while (width / inSampleSize / 2 >= reqWidth
&& height / inSampleSize / 2 >= reqHeight) {
inSampleSize *= 2;
}
Log.i("test", "isSampleSize " + inSampleSize);
return inSampleSize;
结果:
压缩前图片内存中大小是: 63700992K 宽度是: 3456 高度是: 4608
isSampleSize 4
压缩后图片内存中大小是: 3981312K 宽度是: 864 高度是: 1152
最后封装一个图片压缩的工具类: