Volley的ImageRequest中的图片压缩代码
/**
* The real guts of parseNetworkResponse. Broken out for readability.
*/
private Response<Bitmap> doParse(NetworkResponse response) {
byte[] data = response.data;
// 获取图片的边界信息
BitmapFactory.Options decodeOptions = new BitmapFactory.Options();
Bitmap bitmap = null;
// 当最大宽度和最大高度为0时,也就是layout_width和layout_height都设置成wrap_content
// 此时就不做任何压缩处理,直接显示原图
if (mMaxWidth == 0 && mMaxHeight == 0) {
// 设置颜色属性,默认的是 ARGB_8888
decodeOptions.inPreferredConfig = mDecodeConfig;
// 通过decodeByteArray解析方法创建bitmap对象
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
} else {
// If we have to resize this image, first get the natural bounds.
// 通过设置的最大宽高来进行图片压缩
// 禁止为bitmap分配内存,此时返回的bitmap对象为null,
// 虽然bitmap==null,但是BitmapFactory.Options的outWidth、outHeight和outMimeType属性都会被赋值
decodeOptions.inJustDecodeBounds = true;
// 通过decodeByteArray解析方法创建bitmap对象
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
// 获取bitmap对象的真实宽高
int actualWidth = decodeOptions.outWidth;
int actualHeight = decodeOptions.outHeight;
// Then compute the dimensions we would ideally like to decode to.
// 计算出我们所希望的宽高
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight, actualWidth, actualHeight, mScaleType);
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth, actualHeight, actualWidth, mScaleType);
// Decode to the nearest power of two scaling factor.
// 允许为图片分配内存
decodeOptions.inJustDecodeBounds = false;
// TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
// decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED; 此参数已经过时
// 计算出图片的采样率
decodeOptions.inSampleSize = findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);
// 通过decodeByteArray解析方法创建压缩之后的bitmap对象
Bitmap tempBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
// If necessary, scale down to the maximal acceptable size.
// 如果压缩之后图片的宽或者高依旧大于所期望的宽高,将再次对图片进行缩放
if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth || tempBitmap.getHeight() > desiredHeight)) {
// createScaledBitmap的作用是对原来的图片进行压缩之后生成一张新的图片
// 使用createScaledBitmap方法时应该注意,假如图片现在的宽高跟期望的宽高相同时,那将直接返回这张图片,不会生成新的图片。
// 因此在宽高相同的情况下,不应使用此方法,容易造成crash。
// 原因时,返回的还是原先图片,那么在此方法之后调用tempBitmap.recycle(),那么bitmap也就变成null了
bitmap = Bitmap.createScaledBitmap(tempBitmap, desiredWidth, desiredHeight, true);
tempBitmap.recycle();
} else {
bitmap = tempBitmap;
}
}
if (bitmap == null) {
return Response.error(new ParseError(response));
} else {
return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response));
}
}
/**
* Scales one side of a rectangle to fit aspect ratio.
*
* @param maxPrimary Maximum size of the primary dimension (i.e. width for
* max width), or zero to maintain aspect ratio with secondary
* dimension
* @param maxSecondary Maximum size of the secondary dimension, or zero to
* maintain aspect ratio with primary dimension
* @param actualPrimary Actual size of the primary dimension
* @param actualSecondary Actual size of the secondary dimension
* @param scaleType The ScaleType used to calculate the needed image size.
*/
private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
int actualSecondary, ScaleType scaleType) {
// If no dominant value at all, just return the actual.
if ((maxPrimary == 0) && (maxSecondary == 0)) {
return actualPrimary;
}
// If ScaleType.FIT_XY fill the whole rectangle, ignore ratio.
if (scaleType == ScaleType.FIT_XY) {
if (maxPrimary == 0) {
return actualPrimary;
}
return maxPrimary;
}
// If primary is unspecified, scale primary to match secondary's scaling ratio.
if (maxPrimary == 0) {
double ratio = (double) maxSecondary / (double) actualSecondary;
return (int) (actualPrimary * ratio);
}
if (maxSecondary == 0) {
return maxPrimary;
}
double ratio = (double) actualSecondary / (double) actualPrimary;
int resized = maxPrimary;
// If ScaleType.CENTER_CROP fill the whole rectangle, preserve aspect ratio.
if (scaleType == ScaleType.CENTER_CROP) {
if ((resized * ratio) < maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
}
if ((resized * ratio) > maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
}
/**
* Returns the largest power-of-two divisor for use in downscaling a bitmap
* that will not result in the scaling past the desired dimensions.
*
* @param actualWidth Actual width of the bitmap
* @param actualHeight Actual height of the bitmap
* @param desiredWidth Desired width of the bitmap
* @param desiredHeight Desired height of the bitmap
*/
// Visible for testing.
static int findBestSampleSize(
int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
// 计算真实宽度对于期望宽度的倍数,值 wr >= 1
double wr = (double) actualWidth / desiredWidth;
// 计算真实高度对于期望高度的倍数,值 hr >= 1
double hr = (double) actualHeight / desiredHeight;
// 取出两个倍数的最小值最为比例值
double ratio = Math.min(wr, hr);
float n = 1.0f;
// 计算出最终的采样率,如果ratio == 4, 那么最终的 n == 8, n 是 2的倍数
while ((n * 2) <= ratio) {
n *= 2;
}
return (int) n;
}
参考文章: