之前我在读取大量本地图片的时候,是通过一个BitmapFactory.Options来缩放图片然后进行加载。虽然这样处理能快速地处理大堆图片,并且也能减少内存的消耗。但是这样也有坏处。
比如一张 4000*4000 的图片与一张 500*500 在同一个文件夹之下的时候,也通过这种方式进行读取就会产生某些图片解析度很低,有的图片解析度过于高,甚至产生了像素堆积,只有一部分图片是正常的样子。
如下图,可以见到右上角的图片分辨率太低而 右边中间的那第一张图片却分辨率过高。其他的图片倒是没有出现其他问题。 所以为了让绝大多数的图片都能正常地显示,我们需要调整加载策略!
今天我采用了一个不错的方法,能很好地显示我所测试的所有图片的同时也能照顾到内存的使用。所以我就马上爬上来和大家分享分享 QwQ
先说加载策略,如果你看了之后有了想法,请务必自己先实现一次。说不定你的方法比我的更好呢?QAQ
说完了策略,接下来就是代码时间 !!!
/* 获取图片缩略图任务 */
private class GenerateImageThumbnail implements Callable<String>{
private int WIDTH_LIMIT = 500; //宽度限制
private int HEIGHT_LIMIT = 500; //高度限制
private int scaleTimes; //缩放倍数
private String path; //文件路径
private String tag; //图片唯一TAG
private ImageView imageView; //要输出的ImageView
private Bitmap bitmap; //得到的Bitmap对象
private boolean autoScale; //是否自动缩放
private HandleOnLoaded handleOnLoaded; //图片后期处理接口
public GenerateImageThumbnail(String path, String tag, ImageView imageView, int scaleTimes , boolean autoScale , HandleOnLoaded handleOnLoaded) {
this.path = path;
this.tag = tag;
this.imageView = imageView;
this.scaleTimes = scaleTimes;
this.handleOnLoaded = handleOnLoaded;
this.autoScale = autoScale;
}
@Override
public String call() throws Exception {
//先试着读取缓存图片
bitmap = imageCacher.getCache(tag);
if (bitmap != null){
//如果存在缓存,则移除 tag , 然后输出
Log.d("OCImageLoader", "[CACHED] Reduced size Width: "+bitmap.getWidth()+" Height: "+bitmap.getHeight());
cacheExecutor.removeTag(tag);
runOnUIThread(new Runnable() {
@Override
public void run() {
onCompleted(imageView , bitmap);
}
});
return null;
}
//没有缓存,则进行读取操作
//先仅仅读取图片的大小数据
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
bitmap = BitmapFactory.decodeFile(path,options);
int width , height , outWidth , outHeight;
//原始数据
outWidth = options.outWidth;
outHeight = options.outHeight;
//进行缩放计算
if (autoScale){
//自动缩放. 缩放到 长和宽都小于限制 并且大于10单位 为止
scaleTimes = 2;
while (true){
width = outWidth/scaleTimes;
height = outHeight/scaleTimes;
if (width < WIDTH_LIMIT && height < HEIGHT_LIMIT && width >= 10 && height >= 10){
break;
}else {
scaleTimes += 1;
}
}
}else {
//手动缩放,直接应用缩放比例
width = outWidth/scaleTimes;
height = outHeight/scaleTimes;
}
options.inJustDecodeBounds = false;
options.inSampleSize = (outHeight/height + outWidth/width) / 2;
//获得缩放后的图像,以及它的尺寸
bitmap = BitmapFactory.decodeFile(path,options);
width = bitmap.getWidth();
height = bitmap.getHeight();
//计算裁剪区域,以边长最短的边做正方形,并居中裁剪
if (height <= width){
//宽比高长的情况 或 等长的情况下
int startPoint = (width / 2)-(height / 2);
bitmap = Bitmap.createBitmap(bitmap , startPoint , 0 ,height , height);
}else {
//高比宽长的情况下
int startPoint = (height / 2)-(width / 2 );
bitmap = Bitmap.createBitmap(bitmap , 0 , startPoint ,width , width);
}
//如果有需要后期处理缩略图,则进行处理. 否则跳过
if (handleOnLoaded != null){
bitmap = handleOnLoaded.reduce(bitmap,tag);
}
Log.d("OCImageLoader", "Generating thumbnail of :"+path);
Log.d("OCImageLoader", "Original size Width: "+outWidth+" Height: "+outHeight);
Log.d("OCImageLoader", "Reduced size Width: "+bitmap.getWidth()+" Height: "+bitmap.getHeight());
//移除线程池的任务TAG
cacheExecutor.removeTag(tag);
if (bitmap != null){
imageCacher.putCache(tag,bitmap); //缓存图片到LRU以及本地文件
runOnUIThread(new Runnable() {
@Override
public void run() {
onCompleted(imageView , bitmap);
}
});
}
return null;
}
private void onCompleted(ImageView imageView , Bitmap bitmap){
if (bitmap != null && imageView != null && imageView.getTag().equals(tag)){
//图像的输出. 如果imageView是当初指定的那个才输出图像,否则跳过
if (durationMillis > 0 ){
//图像渐入渐出动画
Drawable prevDrawable = imageView.getDrawable();
if (prevDrawable == null) {
prevDrawable = new ColorDrawable(TRANSPARENT);
}
Drawable nextDrawable = new BitmapDrawable(imageView.getResources(), bitmap);
TransitionDrawable transitionDrawable = new TransitionDrawable(
new Drawable[] { prevDrawable, nextDrawable });
imageView.setImageDrawable(transitionDrawable);
transitionDrawable.startTransition(durationMillis);
}else {
imageView.setImageBitmap(bitmap);
}
}
}
}
最后是调用的方法:
/** * 获取本地图片的缩略图 * @param path 文件路径 * @param imageView 图片要显示的ImageView * @param scaleTimes 图片缩放倍数 * @param autoScale 是否自动缩放. 启用自动缩放将忽略 scaleTimes 参数 * @param handleOnLoaded 图片缩略图生成完毕后对缩略图进行处理的接口 */
public void loadImageThumbnail(String path , ImageView imageView , int scaleTimes , boolean autoScale , HandleOnLoaded handleOnLoaded){
File checkFile = new File(path);
if ( checkFile.isFile() && checkFile.canWrite() && imageView != null){
checkFile = null;
if (scaleTimes <= 0){
scaleTimes = 1;
}
imageView.setImageBitmap(null);
imageView.setBackgroundColor(Color.TRANSPARENT);
String tag = buildTag(path+"Thumbnail");
imageView.setTag(tag);
cacheExecutor.submit(new FutureTask<>(new GenerateImageThumbnail(path,tag,imageView,scaleTimes,autoScale,handleOnLoaded)),tag);
}
}
好了 , 现在让我们看看最后的成果~~~
这是二次加载的Log:
512MB RAM 手机内存使用状况 (点击查看大图):
加载的图像界面:
文章里使用到的线程池以及图像缓存等都是基于之前所介绍过的 , 不清楚的dalao可以去前面的看看哦 =.=