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

Android学习笔记之BitmapFactory.Options实现图片资源的加载...

2015-08-18 18:53 357 查看
PS:小项目总算是做完了...历经20多天...素材,设计,以及实现全由自己完成...心力憔悴啊...该写写博客记录一下学习到的东西了...

学习内容:

1.使用BitmapFactory.Options实现图片资源的加载...防止OOM的产生...


我们在设计和制作app的时候,有时我们需要去动态的加载图片资源数据,图片的大小将决定读取图片资源所耗费物理内存...Android对内存的要求是非常苛刻的...如果图片的资源过大,那么就会出现OOM,也就是所说的内存溢出...这样程序就不得不被终止掉...因此我们需要避免这种情况的发生...这里我们需要使用到BitmapFactory.Options...

[b]BitmapFactory.Options其实是一个辅助类,这个类提供了创建Bitmap类的接口,真正完成图像处理的类其实是Bitmap,但是由于这个类的构造函数是私有的,因此是无法在其他的类外进行实例化对象的,因此[b]BitmapFactory.Options充当了这个辅助类,对外暴露接口,这样,我们就可以真正的调用Bitmap中的方法了...[/b][/b]

[b][b] [b][b]BitmapFactory.Options通过decodeFile()的方法去获取图片资源的位置.那么获取到了资源位置后,我们就可以对图片进行解码操作,因为我们都知道,想要获取到一个图片,首先要从图片的码流开始,对码流进行解析才会得到相应的图片...那么这个解码的过程就是使用decodeStream()方法进行解码,解码也是可以限制的...[b][b][b][b][b]BitmapFactory.Options[/b][/b][/b][/b]这个类的[b][b][b][b][b][b][b][b][b]Options其实就是对解码进行一个限制...[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b][b][b][b][b][b][b] 那么再说一下Options,Options有几个属性,第一个inJustDecodeBounds(这是一个布尔值,true和false两个属性),[b][b][b][b][b][b][b][b][b][b][b][b][b][b][b]inJustDecodeBounds[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]=true的时候,那么代表对现在的这张图片进行非完全解码,其实说白了就是不给这个图片资源分配任何的内存,只是获取这个图片的基本信息(比如说:长度和宽度),不分配内存的原因想必大家都知道了,就是防止图片过大的问题,如果图片过大,那么我们获取到图片长度和宽度后,需要对图片进行一个压缩的操作,那么这个压缩就涉及第二个参数,inSampleSize(int 类型,可以是任意值)...这个值的获取取决于我们压缩操作的方式,这个压缩方式是需要我们自己去实现的,最后通过压缩的方式去获取[b][b][b][b][b][b][b][b][b][b][b][b][b][b]inSampleSize[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]的值,通过这个值指定图片缩放的大小...最后将[b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b][b]inJustDecodeBounds[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]=false这样就可以得到缩放后的图片了...如果图片满足正常的范围之内,那么就直接进行显示就可以了...没必要再进行压缩操作...[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b][b][b][b][b][b][b] 还是来一段代码,来方便大家的理解...[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]

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;
}


[b][b][b][b][b][b][b][b][b][b][b][b][b][b] 这里只是粘贴了缩放过程的一个代码,通过这段代码,我们就可以实现大图片缩放为小型图片,防止OOM的发生...这只是一个小的函数,最后我会给出一个源码提供给大家去下载,方便大家去学习...这里还需要说明一些问题...就是这个inSampleSize值的设置.这个值如果设置为2,那么图片就缩放4倍...如果为3,那么缩放的程度就为9倍...以此类推...
[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b][b][b][b][b][b][b] 还有这个decodeStream()方法只是一个把数据封装成流的形式对码流进行一个数据传递...真正完成图片绘制的是其内部过程...我们还是来看一下源代码,方便大家的理解...[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]

/*
*这是源码的实现过程,说实话,我也看不懂所有的东西...因为自己也是个小菜鸟...
*不过我们可以分析一下...
*/
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;
}


[b][b][b][b][b][b][b][b][b][b][b][b][b][b] 我们可以不完全弄懂这个源码到底是怎么回事,但是我们通过源码可以知道其中到底是以怎样的过程进行实现的,这才是看源码的目的...如果还是有更深入研究的读者...那么您可以完全弄懂...这里我们可以看到最后的调用时Bitmap.createScaledBitmap方法,接着[b][b][b][b][b][b][b][b][b][b][b][b][b][b][b]Bitmap.createScaledBitmap[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]方法将调用Bitmap createBitmap...这个方法才是最终实现把图片显示在画布上的一个最终函数,这个函数通过使用Canvas最后在画布上将图片描画出来...这是这个函数的源码,我就不进行解释了,其实这个源码比上面那个更简单,容易理解,其实就是根据这些参数把图片展示出来,比如说获取图片的高度和宽度,缩放的大小,图片的编码格式,最后通过调用Paint画笔,结合Canvas,这里我们能够发现,最后画出来的画布是一个矩形,那么画出来的图片必然也是一个矩形..最后把画出来的图片进行返回..最后就显示在画布之上了...
[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]

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;
}


[b][b][b][b][b][b][b][b][b][b][b][b][b][b] 这样就实现了通过对图片大小的缩放,避免OOM的发生...最后放上面一个源码....提供大家下载....只不过这个源码是通过从服务器上下载图片,通过解析XML文件,然后对数据进行获取..然后对图片进行缩放,最后以ListView的形式进行显示...图片的缩放只隶属于一个小的模块....
[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]

[b][b][b][b][b][b][b][b][b][b][b][b][b][b]源码位置:http://files.cnblogs.com/files/RGogoing/daimarufeng.zip[/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b][/b]
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: