1、概述
优秀的图片加载框架不要太多,什么UIL , Volley ,Picasso,Imageloader等等。但是作为一名合格的程序猿,必须懂其中的实现原理,于是乎,今天我就带大家一起来设计一个加载网络、本地的图片框架。有人可能会说,自己写会不会很渣,运行效率,内存溢出神马的。放心,我们拿demo说话,拼得就是速度,奏事这么任性。
好了,如果你看过之前的博文,类似Android Handler 异步消息处理机制的妙用 创建强大的图片加载类,可能会对接下来文章理解会有很大的帮助。没有的话,就跟我往下继续走吧,也不要去看了。
关于加载本地图片,当然了,我手机图片比较少,7000来张:
1、首先肯定不能内存溢出,但是尼玛现在像素那么高,怎么才能保证呢?我相信利用LruCache统一管理你的图片是个不二的选择,所有的图片从LruCache里面取,保证所有的图片的内存不会超过预设的空间。
2、加载速度要刚刚的,我一用力,滑动到3000张的位置,你要是还在从第一张给我加载,尼玛,你以为我打dota呢。所以我们需要引入加载策略,我们不能FIFO,我们选择LIFO,当前呈现给用户的,最新加载;当前未呈现的,选择加载。
3、使用方便。一般图片都会使用GridView作为控件,在getView里面进行图片加载,当然了为了不错乱,可能还需要用户去自己setTag,自己写回调设置图片。当然了,我们不需要这么麻烦,一句话IoadImage(imageview,path)即可,剩下的请交给我们的图片加载框架处理。
做到以上几点,关于本地的图片加载应该就木有什么问题了。
关于加载网络图片,其实原理差不多,就多了个是否启用硬盘缓存的选项,如果启用了,加载时,先从内存中查找,然后从硬盘上找,最后去网络下载。下载完成后,别忘了写入硬盘,加入内存缓存。如果没有启用,那么就直接从网络压缩获取,加入内存即可。
2、效果图
终于扯完了,接下来,简单看个效果图,关于加载本地图片的效果图:可以从Android 超高仿微信图片选择器 图片该这么加载这篇博客中下载Demo运行。
下面演示一个网络加载图片的例子:
80多张从网络加载的图片,可以看到我直接拖到最后,基本是呈现在用户眼前的最先加载,要是从第一张到80多,估计也是醉了。
此外:图片来自老郭的博客,感谢!!!ps:如果你觉得图片不劲爆,Day Day Up找老郭去。
3、完全解析
1、关于图片的压缩
不管是从网络还是本地的图片,加载都需要进行压缩,然后显示:
用户要你压缩显示,会给我们什么?一个imageview,一个path,我们的职责就是压缩完成后显示上去。
1、本地图片的压缩
a、获得imageview想要显示的大小
想要压缩,我们第一步应该是获得imageview想要显示的大小,没大小肯定没办法压缩?
那么如何获得imageview想要显示的大小呢?
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
/** * 根据ImageView获适当的压缩的宽和高 * * @param imageView * @return */ public static ImageSize getImageViewSize(ImageView imageView) { ImageSize imageSize = new ImageSize(); DisplayMetrics displayMetrics = imageView.getContext().getResources() .getDisplayMetrics(); LayoutParams lp = imageView.getLayoutParams(); int width = imageView.getWidth();// 获取imageview的实际宽度 if (width <= 0) { width = lp.width;// 获取imageview在layout中声明的宽度 } if (width <= 0) { // width = imageView.getMaxWidth();// 检查最大值 width = getImageViewFieldValue(imageView, "mMaxWidth"); } if (width <= 0) { width = displayMetrics.widthPixels; } int height = imageView.getHeight();// 获取imageview的实际高度 if (height <= 0) { height = lp.height;// 获取imageview在layout中声明的宽度 } if (height <= 0) { height = getImageViewFieldValue(imageView, "mMaxHeight");// 检查最大值 } if (height <= 0) { height = displayMetrics.heightPixels; } imageSize.width = width; imageSize.height = height; return imageSize; } public static class ImageSize { int width; int height; } |
可以看到,我们拿到imageview以后:
首先企图通过getWidth获取显示的宽;有些时候,这个getWidth返回的是0;
那么我们再去看看它有没有在布局文件中书写宽;
如果布局文件中也没有精确值,那么我们再去看看它有没有设置最大值;
如果最大值也没设置,那么我们只有拿出我们的终极方案,使用我们的屏幕宽度;
总之,不能让它任性,我们一定要拿到一个合适的显示值。
可以看到这里或者最大宽度,我们用的反射,而不是getMaxWidth();维萨呢,因为getMaxWidth竟然要API 16,我也是醉了;为了兼容性,我们采用反射的方案。反射的代码就不贴了。
b、设置合适的inSampleSize
我们获得想要显示的大小,为了什么,还不是为了和图片的真正的宽高做比较,拿到一个合适的inSampleSize,去对图片进行压缩么。
那么首先应该是拿到图片的宽和高:
1 2 3 4 |
// 获得图片的宽和高,并不把图片加载到内存中 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeFile(path, options); |
这三行就成功获取图片真正的宽和高了,存在我们的options里面;
然后我们就可以happy的去计算inSampleSize了:
1 2 3 4 5 6 7 8 9 10 11 12 13 ױ写会不会很渣,运行效率,内存溢出神马的。放心,我们拿demo说话,拼得就是速度,奏事这么任性。
好了,如果你看过之前的博文,类似Android Handler 异步消息处理机制的妙用 创建强大的图片加载类,可能会对接下来文章理解会有很大的帮助。没有的话,就跟我往下继续走吧,也不要去看了。 关于加载本地图片,当然了,我手机图片比较少,7000来张: 1、首先肯定不能内存溢出,但是尼玛现在像素那么高,怎么才能保证呢?我相信利用LruCache统一管理你的图片是个不二的选择,所有的图片从LruCache里面取,保证所有的图片的内存不会超过预设的空间。 2、加载速度要刚刚的,我一用力,滑动到3000张的位置,你要是还在从第一张给我加载,尼玛,你以为我打dota呢。所以我们需要引入加载策略,我们不能FIFO,我们选择LIFO,当前呈现给用户的,最新加载;当前未呈现的,选择加载。 3、使用方便。一般图片都会使用GridView作为控件,在getView里面进行图片加载,当然了为了不错乱,可能还需要用户去自己setTag,自己写回调设置图片。当然了,我们不需要这么麻烦,一句话IoadImage(imageview,path)即可,剩下的请交给我们的图片加载框架处理。 做到以上几点,关于本地的图片加载应该就木有什么问题了。 关于加载网络图片,其实原理差不多,就多了个是否启用硬盘缓存的选项,如果启用了,加载时,先从内存中查找,然后从硬盘上找,最后去网络下载。下载完成后,别忘了写入硬盘,加入内存缓存。如果没有启用,那么就直接从网络压缩获取,加入内存即可。 2、效果图终于扯完了,接下来,简单看个效果图,关于加载本地图片的效果图:可以从Android 超高仿微信图片选择器 图片该这么加载这篇博客中下载Demo运行。 下面演示一个网络加载图片的例子: 80多张从网络加载的图片,可以看到我直接拖到最后,基本是呈现在用户眼前的最先加载,要是从第一张到80多,估计也是醉了。 此外:图片来自老郭的博客,感谢!!!ps:如果你觉得图片不劲爆,Day Day Up找老郭去。 3、完全解析1、关于图片的压缩不管是从网络还是本地的图片,加载都需要进行压缩,然后显示: 用户要你压缩显示,会给我们什么?一个imageview,一个path,我们的职责就是压缩完成后显示上去。 1、本地图片的压缩a、获得imageview想要显示的大小 想要压缩,我们第一步应该是获得imageview想要显示的大小,没大小肯定没办法压缩? 那么如何获得imageview想要显示的大小呢?
可以看到,我们拿到imageview以后: 首先企图通过getWidth获取显示的宽;有些时候,这个getWidth返回的是0; 那么我们再去看看它有没有在布局文件中书写宽; 如果布局文件中也没有精确值,那么我们再去看看它有没有设置最大值; 如果最大值也没设置,那么我们只有拿出我们的终极方案,使用我们的屏幕宽度; 总之,不能让它任性,我们一定要拿到一个合适的显示值。 可以看到这里或者最大宽度,我们用的反射,而不是getMaxWidth();维萨呢,因为getMaxWidth竟然要API 16,我也是醉了;为了兼容性,我们采用反射的方案。反射的代码就不贴了。 b、设置合适的inSampleSize 我们获得想要显示的大小,为了什么,还不是为了和图片的真正的宽高做比较,拿到一个合适的inSampleSize,去对图片进行压缩么。 那么首先应该是拿到图片的宽和高:
这三行就成功获取图片真正的宽和高了,存在我们的options里面; 然后我们就可以happy的去计算inSampleSize了: |