Android学习笔记之BitmapFactory.Options实现图片资源的加载...
PS:小项目总算是做完了...历经20多天...素材,设计,以及实现全由自己完成...心力憔悴啊...该写写博客记录一下学习到的东西了...
学习内容:
1.使用BitmapFactory.Options实现图片资源的加载...防止OOM的产生...
我们在设计和制作app的时候,有时我们需要去动态的加载图片资源数据,图片的大小将决定读取图片资源所耗费物理内存...Android对内存的要求是非常苛刻的...如果图片的资源过大,那么就会出现OOM,也就是所说的内存溢出...这样程序就不得不被终止掉...因此我们需要避免这种情况的发生...这里我们需要使用到BitmapFactory.Options...
BitmapFactory.Options其实是一个辅助类,这个类提供了创建Bitmap类的接口,真正完成图像处理的类其实是Bitmap,但是由于这个类的构造函数是私有的,因此是无法在其他的类外进行实例化对象的,因此BitmapFactory.Options充当了这个辅助类,对外暴露接口,这样,我们就可以真正的调用Bitmap中的方法了...
BitmapFactory.Options通过decodeFile()的方法去获取图片资源的位置.那么获取到了资源位置后,我们就可以对图片进行解码操作,因为我们都知道,想要获取到一个图片,首先要从图片的码流开始,对码流进行解析才会得到相应的图片...那么这个解码的过程就是使用decodeStream()方法进行解码,解码也是可以限制的...BitmapFactory.Options这个类的Options其实就是对解码进行一个限制...
那么再说一下Options,Options有几个属性,第一个inJustDecodeBounds(这是一个布尔值,true和false两个属性),inJustDecodeBounds=true的时候,那么代表对现在的这张图片进行非完全解码,其实说白了就是不给这个图片资源分配任何的内存,只是获取这个图片的基本信息(比如说:长度和宽度),不分配内存的原因想必大家都知道了,就是防止图片过大的问题,如果图片过大,那么我们获取到图片长度和宽度后,需要对图片进行一个压缩的操作,那么这个压缩就涉及第二个参数,inSampleSize(int 类型,可以是任意值)...这个值的获取取决于我们压缩操作的方式,这个压缩方式是需要我们自己去实现的,最后通过压缩的方式去获取inSampleSize的值,通过这个值指定图片缩放的大小...最后将inJustDecodeBounds=false这样就可以得到缩放后的图片了...如果图片满足正常的范围之内,那么就直接进行显示就可以了...没必要再进行压缩操作...
还是来一段代码,来方便大家的理解...
private Bitmap decodeFile(File f){
try {
//解码图像大小,对图片进行缩放...防止图片过大导致内存溢出...
BitmapFactory.Options o = new BitmapFactory.Options();//实例化一个对象... o.inJustDecodeBounds = true;//这个就是Options的第一个属性,设置为true的时候,不会完全的对图片进行解码操作,不会为其分配内存,只是获取图片的基本信息... BitmapFactory.decodeStream(new FileInputStream(f),null,o); //以码流的形式进行解码.... /*
* 下面也就是对图片进行的一个压缩的操作...如果图片过大,最后会根据指定的数值进行缩放...
* 找到正确的刻度值,它应该是2的幂.
* 这里我指定了图片的长度和宽度为70个像素...
*
* */ final int REQUIRED_SIZE=70;
int width_tmp=o.outWidth, height_tmp=o.outHeight;
int scale=1;
while(true){
if(width_tmp/2<REQUIRED_SIZE || height_tmp/2<REQUIRED_SIZE)
break;
width_tmp/=2;
height_tmp/=2;
scale*=2;
} BitmapFactory.Options o2 = new BitmapFactory.Options(); //这里定义了一个新的对象...获取的还是同一张图片...
o2.inSampleSize=scale; //对这张图片设置一个缩放值...inJustDecodeBounds不需要进行设置...
return BitmapFactory.decodeStream(new FileInputStream(f), null, o2); //这样通过这个方法就可以产生一个小的图片资源了...
} catch (Exception e) {}
return null;
}
这里只是粘贴了缩放过程的一个代码,通过这段代码,我们就可以实现大图片缩放为小型图片,防止OOM的发生...这只是一个小的函数,最后我会给出一个源码提供给大家去下载,方便大家去学习...这里还需要说明一些问题...就是这个inSampleSize值的设置.这个值如果设置为2,那么图片就缩放4倍...如果为3,那么缩放的程度就为9倍...以此类推...
还有这个decodeStream()方法只是一个把数据封装成流的形式对码流进行一个数据传递...真正完成图片绘制的是其内部过程...我们还是来看一下源代码,方便大家的理解...
/*
*这是源码的实现过程,说实话,我也看不懂所有的东西...因为自己也是个小菜鸟...
*不过我们可以分析一下...
*/
public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
// we don't throw in this case, thus allowing the caller to only check
// the cache, and not force the image to be decoded.
if (is == null) {
return null;
} // we need mark/reset to work properly if (!is.markSupported()) {
is = new BufferedInputStream(is, DECODE_BUFFER_SIZE);
} // so we can call reset() if a given codec gives up after reading up to
// this many bytes. FIXME: need to find out from the codecs what this
// value should be.
is.mark(1024); Bitmap bm;
boolean finish = true;
/*
*我们来看下面这个函数...
*/
if (is instanceof AssetManager.AssetInputStream) {//这块是一个判断的过程,判断的东西就是,我们这个图片资源到底来自于什么地方,如果满足这个if条件,那么这个图片资源属于drawable文件下的资源...
final int asset = ((AssetManager.AssetInputStream) is).getAssetInt();
/*
*这里就是设置缩放的一个过程,其中包含一些参数的设置...
*比如说缩放的参数,以及目标面积的大小设定...
*/
if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
float scale = 1.0f;
int targetDensity = 0;
if (opts != null) {
final int density = opts.inDensity;
targetDensity = opts.inTargetDensity;
if (density != 0 && targetDensity != 0) {
scale = targetDensity / (float) density;
}
}
/*
*下面这个函数的源码,我也看不到了...应该就是一个解码的操作过程...
*/
bm = nativeDecodeAsset(asset, outPadding, opts, true, scale);
if (bm != null && targetDensity != 0) bm.setDensity(targetDensity); finish = false;
} else {
/*
*这块就是直接解码操作,这个else满足的条件是图片资源满足指定的大小,因此我们不需要传递scale参数了...也就是代表不用进行缩放...
*/
bm = nativeDecodeAsset(asset, outPadding, opts);
}
} else { //这里满足的条件想必大家知道,就是图片资源可能来自于其他地方...
// pass some temp storage down to the native code. 1024 is made up,
// but should be large enough to avoid too many small calls back
// into is.read(...) This number is not related to the value passed
// to mark(...) above.
/*
*通过byte流的形式对图片进行获取,这个也很好理解,如果想获取图片资源,那么必须要经过流的形式,对资源数据进行封装...然后获取...
*上面之所以不用流的形式,是因为drawable中的资源有特定的获取方式...
*/
byte [] tempStorage = null;
if (opts != null) tempStorage = opts.inTempStorage;
if (tempStorage == null) tempStorage = new byte[16 * 1024]; /*
*还是同理,设置参数....
*/
if (opts == null || (opts.inScaled && opts.inBitmap == null)) {
float scale = 1.0f;
int targetDensity = 0;
if (opts != null) {
final int density = opts.inDensity;
targetDensity = opts.inTargetDensity;
if (density != 0 && targetDensity != 0) {
scale = targetDensity / (float) density;
}
} bm = nativeDecodeStream(is, tempStorage, outPadding, opts, true, scale);
if (bm != null && targetDensity != 0) bm.setDensity(targetDensity); finish = false;
} else {
bm = nativeDecodeStream(is, tempStorage, outPadding, opts);
}
} if (bm == null && opts != null && opts.inBitmap != null) {
throw new IllegalArgumentException("Problem decoding into existing bitmap");
}
//这个方法,将会被最终调用..也就是下面的方法...
return finish ? finishDecode(bm, outPadding, opts) : bm;
}
/*
*这个方法是当完成解码操作后需要调用的方法...
*/
private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
if (bm == null || opts == null) {
return bm;
} final int density = opts.inDensity;
if (density == 0) {
return bm;
} bm.setDensity(density);
final int targetDensity = opts.inTargetDensity;
if (targetDensity == 0 || density == targetDensity || density == opts.inScreenDensity) {
return bm;
}
byte[] np = bm.getNinePatchChunk();
int[] lb = bm.getLayoutBounds();
final boolean isNinePatch = np != null && NinePatch.isNinePatchChunk(np);
/*
*这个函数重要部分就是下面,我们可以看到,通过对参数的一些设定..最后会调用 Bitmap.createScaledBitmap方法...
*/
if (opts.inScaled || isNinePatch) {
float scale = targetDensity / (float) density;
if (scale != 1.0f) {
final Bitmap oldBitmap = bm;
bm = Bitmap.createScaledBitmap(oldBitmap,
Math.max(1, (int) (bm.getWidth() * scale + 0.5f)),
Math.max(1, (int) (bm.getHeight() * scale + 0.5f)), true);
if (bm != oldBitmap) oldBitmap.recycle(); if (isNinePatch) {
np = nativeScaleNinePatch(np, scale, outPadding);
bm.setNinePatchChunk(np);
}
if (lb != null) {
int[] newLb = new int[lb.length];
for (int i=0; i<lb.length; i++) {
newLb[i] = (int)((lb[i]*scale)+.5f);
}
bm.setLayoutBounds(newLb);
}
} bm.setDensity(targetDensity);
} return bm;
}
我们可以不完全弄懂这个源码到底是怎么回事,但是我们通过源码可以知道其中到底是以怎样的过程进行实现的,这才是看源码的目的...如果还是有更深入研究的读者...那么您可以完全弄懂...这里我们可以看到最后的调用时Bitmap.createScaledBitmap方法,接着Bitmap.createScaledBitmap方法将调用Bitmap createBitmap...这个方法才是最终实现把图片显示在画布上的一个最终函数,这个函数通过使用Canvas最后在画布上将图片描画出来...这是这个函数的源码,我就不进行解释了,其实这个源码比上面那个更简单,容易理解,其实就是根据这些参数把图片展示出来,比如说获取图片的高度和宽度,缩放的大小,图片的编码格式,最后通过调用Paint画笔,结合Canvas,这里我们能够发现,最后画出来的画布是一个矩形,那么画出来的图片必然也是一个矩形..最后把画出来的图片进行返回..最后就显示在画布之上了...
public static Bitmap createBitmap(Bitmap source, int x, int y, int width, int height,
Matrix m, boolean filter) { checkXYSign(x, y);
checkWidthHeight(width, height);
if (x + width > source.getWidth()) {
throw new IllegalArgumentException("x + width must be <= bitmap.width()");
}
if (y + height > source.getHeight()) {
throw new IllegalArgumentException("y + height must be <= bitmap.height()");
} // check if we can just return our argument unchanged
if (!source.isMutable() && x == 0 && y == 0 && width == source.getWidth() &&
height == source.getHeight() && (m == null || m.isIdentity())) {
return source;
} int neww = width;
int newh = height;
Canvas canvas = new Canvas();
Bitmap bitmap;
Paint paint; Rect srcR = new Rect(x, y, x + width, y + height);
RectF dstR = new RectF(0, 0, width, height); Config newConfig = Config.ARGB_8888;
final Config config = source.getConfig();
// GIF files generate null configs, assume ARGB_8888
if (config != null) {
switch (config) {
case RGB_565:
newConfig = Config.RGB_565;
break;
case ALPHA_8:
newConfig = Config.ALPHA_8;
break;
//noinspection deprecation
case ARGB_4444:
case ARGB_8888:
default:
newConfig = Config.ARGB_8888;
break;
}
} if (m == null || m.isIdentity()) {
bitmap = createBitmap(neww, newh, newConfig, source.hasAlpha());
paint = null; // not needed
} else {
final boolean transformed = !m.rectStaysRect(); RectF deviceR = new RectF();
m.mapRect(deviceR, dstR); neww = Math.round(deviceR.width());
newh = Math.round(deviceR.height()); bitmap = createBitmap(neww, newh, transformed ? Config.ARGB_8888 : newConfig,
transformed || source.hasAlpha()); canvas.translate(-deviceR.left, -deviceR.top);
canvas.concat(m); paint = new Paint();
paint.setFilterBitmap(filter);
if (transformed) {
paint.setAntiAlias(true);
}
} // The new bitmap was created from a known bitmap source so assume that
// they use the same density
bitmap.mDensity = source.mDensity; canvas.setBitmap(bitmap);
canvas.drawBitmap(source, srcR, dstR, paint);
canvas.setBitmap(null); return bitmap;
}
这样就实现了通过对图片大小的缩放,避免OOM的发生...最后放上面一个源码....提供大家下载....只不过这个源码是通过从服务器上下载图片,通过解析XML文件,然后对数据进行获取..然后对图片进行缩放,最后以ListView的形式进行显示...图片的缩放只隶属于一个小的模块....
源码位置:http://files.cnblogs.com/files/RGogoing/daimarufeng.zip
Android学习笔记之BitmapFactory.Options实现图片资源的加载...的更多相关文章
- Android 学习笔记之Volley(七)实现Json数据加载和解析...
学习内容: 1.使用Volley实现异步加载Json数据... Volley的第二大请求就是通过发送请求异步实现Json数据信息的加载,加载Json数据有两种方式,一种是通过获取Json对象,然后 ...
- Redis深入学习笔记(一)Redis启动数据加载流程
这两年使用Redis从单节点到主备,从主备到一主多从,再到现在使用集群,碰到很多坑,所以决定深入学习下Redis工作原理并予以记录. 本系列主要记录了Redis工作原理的一些要点,当然配置搭建和使用这 ...
- Windows调试学习笔记:(一)WinDBG中加载SOS和CLR
最近产品环境出现了部分服务器当机情况,虽然重启之后问题就不出现了.但本着彻底解决问题的态度,想要找到root cause.多次尝试Visual Studio失败(可能是代码惊醒了优化和签名)之后,决定 ...
- webpack学习笔记—优化缓存、合并、懒加载等
除了前面的webpack基本配置,还可以进一步添加配置,优化合并文件,加快编译速度.下面是生产环境配置文件webpack.production.js,与wenbpack.config.js相比其不需要 ...
- ES6学习笔记(二十)Module 的加载实现
上一章介绍了模块的语法,本章介绍如何在浏览器和 Node 之中加载 ES6 模块,以及实际开发中经常遇到的一些问题(比如循环加载). 1.浏览器加载 传统方法 HTML 网页中,浏览器通过<sc ...
- AntDesign vue学习笔记(五)导航菜单动态加载
一般的后台系统都有一个树形导航菜单,具体实现如下,主要参考https://my.oschina.net/u/4131669/blog/3048416 "menuList": [ { ...
- 【转】 Pro Android学习笔记(二二):用户界面和控制(10):自定义Adapter
目录(?)[-] 设计Adapter的布局 代码部分 Activity的代码 MyAdapter的代码数据源和构造函数 MyAdapter的代码实现自定义的adapter MyAdapter的代码继续 ...
- Android学习笔记进阶之在图片上涂鸦(能清屏)
Android学习笔记进阶之在图片上涂鸦(能清屏) 2013-11-19 10:52 117人阅读 评论(0) 收藏 举报 HandWritingActivity.java package xiaos ...
- Android学习笔记_51_转android 加载大图片防止内存溢出
首先来还原一下堆内存溢出的错误.首先在SD卡上放一张照片,分辨率为(3776 X 2520),大小为3.88MB,是我自己用相机拍的一张照片.应用的布局很简单,一个Button一个ImageView, ...
随机推荐
- [C/C++] zltabout(带缩进的格式化输出)v1.0。能以相同的代码绑定到 C FILE 或 C++流
作者:zyl910 一.缘由 在写一些生成文本的程序时,经常需要使用带缩进的格式化输出的功能.以前为此写过不少类似的函数,可惜它们的可重用性很差. 这是因为——1) C语言的FILE*不支持重定向到自 ...
- Hadoop - Mac OSX下配置和启动hadoop以及常见错误解决
0. 安装JDK 参考网上教程在OSX下安装jdk 1. 下载及安装hadoop a) 下载地址: http://hadoop.apache.org b) 配置ssh环境 在terminal里面输入: ...
- C# inline-hook / api-hook
我查阅了一下相关C#方面的资料,却没有发现有提供过关于api-hook方面的资 料包括应用库由此本人编写一套inline-hook的库用于支持x64.x86上的基于在 clr的公共语言,如: c#.c ...
- Three Sources of a Solid Object-Oriented Design
pingback :http://java.sys-con.com/node/84633?page=0,1 Object-oriented design is like an alloy consis ...
- hibernate 映射 多对一
一对多和上文讲的多对一两种映射关系,其实就是站在相反的角度考虑同样的事情. 一对多和多对一映射原理是一样的,都在多的一端加入一个外键指向一的一端.也就是说,在关系数据库的表中,他们的表及表字段都是一样 ...
- Django 源码小剖: Django 对象关系映射(ORM)
引 从前面已经知道, 一个 request 的到来和一个对应 response 的返回的流程, 数据处理和数据库离不开. 我们也经常在 views.py 的函数定义中与数据库打交道. django O ...
- QQ空间直播秒开优化实践[读]
http://mp.weixin.qq.com/s?__biz=MzI1MTA1MzM2Nw==&mid=2649796799&idx=1&sn=42061b7d021b8d8 ...
- Asp.net中的ajax回调模式(ICallbackEventHandler)
客户端回调本质上就是指通过前端的客户端脚本向服务器端传递相应的数据参数,服务器端再以接受到的参数进行查询和处理,最后将结果回传到客户端进行显示.asp.net 2.0提供了实现无刷新回调的接口ICal ...
- 获取发布版SHA1获取
- [原创]android自定义控件的最大高度MaxHeightView
代码地址:https://github.com/Carbs0126/MaxHeightView android中部分控件具有maxHeight功能,如button等,但是对于ViewGroup类的控件 ...