您的位置:首页 > 移动开发 > Android开发

Android 图片如何高效加载与缓存 (4.5) —— 缩略图生成的优化

2016-04-17 22:42 555 查看
在之前那篇里面仍有不足之处,比如:

一张4000*3000的图片压缩到了300*200的大小,然后输出到ImageView上。但是ImageView的大小可能只有100*100,这样我们仍然占用了很多不需要的内存。所以我们只需要将图片居中裁剪成ImageView的大小即可。

知道了如何做之后,在考虑一下可能存在的图片尺寸与ImageView尺寸之间的关系:

1.图片的宽 小于 ImageView的宽 (比如:5000*10)

2.图片的高 小于 ImageView的高 (比如:10*5000)

3.图片的宽和高都 小于 ImageView的宽高 (比如:1*1)

考虑好了之后,我们就能动手写代码了!

先来个大合照,然后我在把裁剪的方法一一拿出来说吧 = n =

/*
获取图片缩略图任务
*/
private class GenerateImageThumbnail implements Callable<String>{

private int scaleTimes;  //缩放倍数
private String path;  //图片路径
private String tag;  //图片加载任务唯一TAG
private ImageView imageView;  //图片接收的ImageView
private Bitmap bitmap;  //图片的Bitmap对象
private HandleOnLoaded handleOnLoaded;  //图片效果处理回调

public GenerateImageThumbnail(String path, String tag, ImageView imageView, HandleOnLoaded handleOnLoaded) {
this.path = path;
this.tag = tag;
this.imageView = imageView;
this.handleOnLoaded = handleOnLoaded;
}

@Override
public String call() throws Exception {

try {
//进行图片剪裁
bitmap = cropBitmap();
} catch (IllegalArgumentException e) {
//如果裁剪出现了异常
fetherExecutor.removeTag(tag);
Log.d("OCImageLoader", "Exception on croping bitmap. "+e);
runOnUIThread(new Runnable() {
@Override
public void run() {
onError();
}
});
return null;
}

if (handleOnLoaded != null){
//如果有需要进行图片处理,则使用回调处理
bitmap = handleOnLoaded.reduce(bitmap,tag);
}

if (bitmap != null){
//图片进行缓存
imageCacher.putCache(tag,bitmap);
runOnUIThread(new Runnable() {
@Override
public void run() {
onCompleted(imageView , bitmap);
}
});
}
fetherExecutor.removeTag(tag);

return null;
}

//加载完成的操作
private void onCompleted(ImageView imageView , Bitmap bitmap){
...
}

//加载失败的操作
private void onError(){
...
}

/**
* 进行 Bitmap 的裁剪缩放操作
* @return  处理后的Bitmap
*/
private Bitmap cropBitmap() throws IllegalArgumentException{
...
}

/**
* 普通裁剪
* @return  裁剪后的Bitmap
*/
private Bitmap normalCropBitmap(int pictureWidth , int pictureHeight , int showHeight , int showWidth , BitmapFactory.Options options)
throws IllegalArgumentException{
...
}

/**
* 高度裁剪
* @return  裁剪后的Bitmap
*/
private Bitmap heightCropBitmap(int pictureWidth , int pictureHeight , int showHeight , BitmapFactory.Options options)
throws IllegalArgumentException{
...
}

/**
* 长度裁剪
* @return  裁剪后的Bitmap
*/
private Bitmap widthCropBitmap(int pictureWidth , int pictureHeight , int showWidth , BitmapFactory.Options options)
throws IllegalArgumentException{
...
}
}


方法 cropBitmap() 用于初始化数据 以及判断应该如何裁剪

private Bitmap cropBitmap() throws IllegalArgumentException{
//默认起始缩放倍数
scaleTimes = 2;

//先仅仅加载图片的尺寸数据
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeFile(path,options);

//裁剪方式
int cropType;

//原图尺寸
int pictureWidth = options.outWidth;
int pictureHeight = options.outHeight;

//ImageView尺寸. 先尽可能缩放成这个尺寸
int showWidth = imageView.getMeasuredWidth();
int showHeight = imageView.getMeasuredHeight();

if (showHeight * showWidth == 0){
//如果有一项ImageView尺寸的数据为 0 , 则赋予默认值. 裁剪为普通状态
showHeight = 120;
showWidth = 120;
cropType = 0;
}else if ( pictureHeight < showHeight && pictureWidth < showWidth ){
//如果原图小于要显示的尺寸,则直接返回原图对象
return BitmapFactory.decodeFile(path);
}else if ( pictureHeight < showHeight ){
//如果原图的高度小于要显示的尺寸,就直接将  原图的长度  进行裁剪
cropType = 1;
}else if ( pictureWidth < showWidth ){
//如果原图的宽度小于要显示的尺寸,就直接将  原图的高度  进行裁剪
cropType = 2;
}else {
//普通状态
cropType = 0;
}

switch (cropType){
case 0:
//普通裁剪
bitmap = normalCropBitmap(pictureWidth,pictureHeight,showHeight,showWidth,options);
break;
case 1:
//长度裁剪
bitmap = widthCropBitmap(pictureWidth,pictureHeight,showWidth,options);
break;
case 2:
//高度裁剪
bitmap = heightCropBitmap(pictureWidth,pictureHeight,showHeight,options);
break;
default:
bitmap = normalCropBitmap(pictureWidth,pictureHeight,showHeight,showWidth,options);
break;
}

return bitmap;
}


方法 normalCropBitmap() 默认裁剪方式

/**
* 普通裁剪
* @return  裁剪后的Bitmap
*/
private Bitmap normalCropBitmap(int pictureWidth , int pictureHeight , int showHeight , int showWidth , BitmapFactory.Options options)
throws IllegalArgumentException{

//临时存储计算得到的上一次结果,预设为图片原始尺寸.
int reducedWidth = pictureWidth;
int reducedHeight = pictureHeight;

int lastWidth;
int lastHeight;
while (true){

lastWidth = pictureWidth/scaleTimes;
lastHeight = pictureHeight/scaleTimes;

if ( lastHeight < showHeight || lastWidth < showWidth){
//如果计算得到的尺寸小于要得到的尺寸,则跳出
break;
}else {
//否则就继续增大缩放倍数,同时记录这次的尺寸
reducedWidth = lastWidth;
reducedHeight = lastHeight;
scaleTimes += 1;
}

}

//用得出的尺寸数据,计算出缩放倍数,然后读取图片
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
options.inSampleSize = (pictureHeight / reducedHeight + pictureWidth / reducedWidth) /2;

//获取等待截取的Bitmap
Bitmap pictureBitmap = BitmapFactory.decodeFile(path,options);

//计算裁剪的起点
int cropX , cropY;
cropX = (reducedWidth/2)-(showWidth/2);
cropY = (reducedHeight/2)-(showHeight/2);

//剪!
pictureBitmap = Bitmap.createBitmap(pictureBitmap,cropX,cropY,showWidth,showHeight);

return pictureBitmap;
}


方法 heightCropBitmap() 高度的裁剪

/**
* 高度裁剪
* @return  裁剪后的Bitmap
*/
private Bitmap heightCropBitmap(int pictureWidth , int pictureHeight , int showHeight , BitmapFactory.Options options)
throws IllegalArgumentException{
//图片不用缩放了,计算下裁剪的居中位置,就能直接裁剪了.下一个方法也一样.
int cropY = ( pictureHeight / 2 ) - ( showHeight / 2 );
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
bitmap = BitmapFactory.decodeFile(path,options);
bitmap = Bitmap.createBitmap(bitmap,0,cropY,pictureWidth,showHeight);
return bitmap;
}


方法 widthCropBitmap() 宽度的裁剪

/**
* 宽度裁剪
* @return  裁剪后的Bitmap
*/
private Bitmap widthCropBitmap(int pictureWidth , int pictureHeight , int showWidth , BitmapFactory.Options options)
throws IllegalArgumentException{
int cropX = ( pictureWidth / 2 ) - ( showWidth / 2 );
options.inJustDecodeBounds = false;
options.inPreferredConfig = Bitmap.Config.RGB_565;
bitmap = BitmapFactory.decodeFile(path,options);
bitmap = Bitmap.createBitmap(bitmap,cropX,0,showWidth,pictureHeight);
return bitmap;
}


下面是正常向的图片处理之后的Log 可以看到无论图片尺寸是如何的,处理后的大小都是统一的



下面这是特殊情况的log

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息