Bitmap的高效加载

前言

在具体阐述如何高效加载bitmap图片前,我们需要先想清楚一个问题是为什么要注重bitmap的高效加载?由于Bitmap的特殊性以及Android对单个应用施加的内存限制,比如16MB,这就导致加载Bitmap的时候很容易出现内存溢出。下面这个异常应该在开发中经常遇到:

java.lang.OutofMemoryError:bitmap size exceds VM budget

因此如何高效的加载Bitmap就变得非常重要了。

Bitmap的高效加载

在介绍Bitmap的高效加载之前,先说一下如何加载一个Bitmap,Bitmap在Android中指的是一张图片,可以是png格式也可以是jpg等其他常见的图片格式。

###那么如何加载一个图片呢?

BitmapFactory类提供了四类方法:decodeFile,decodeResource,decodeStream和decodeByteArray,分别用于支持从文件系统,资源,输入流,以及字节数组中加载一个Bitmap对象,其中decodeFile和decodeResource又间接调用了decodeStream方法,这四类方法最终是在Android的底层实现的,对应着BitmapFactory类的几个native方法。

如何高效加载Bitmap呢?

其实核心思想也很简单,那就是采用BitmapFactory.Options来加载所需尺寸的图片。这里假设通过ImageView来显示图片,很多时候ImageView并没有图片的原始尺寸那么大,这个时候把整个图片加载进来后在设给ImageView,这显然是没有必要的,因为ImageView并没有办法显示原始的图片。通过BitmapFactory.Options就可以按一定的采样比率来加载缩小后的图片,将缩小后的图片在ImageView上显示,这样就会降低内存占用从而在一定程度上避免OOM,提高了Bitmap加载时的性能。BitmapFactory提供的加载图片的四类方法都支持BitmapFactory.Option参数,通过它们就可以很方便地对一个图片进行采样缩放。

通过BitmapFactory.Option来缩放图片,主要是用了它的inSampleSize参数,即采样率。当inSampleSize为1时,采样后的图片大小为图片的原始大小,当inSampleSize大于1时,比如2,那么采样后的图片其宽/高均为原图大小的1/2,而像素数为原图的1/4,其占用内存大小也为原图的1/4。拿一张10241024像素的图片来说,假定采用ARGB8888格式存储,那么占用的内存为102410244,即4MB,如果inSampleSize为2,那么采样后的图片其内存占用只有512512*4,即1MB。可以发现采样率inSampleSize必须是大于1的整数图片才会有缩小的效果,并且采样率同样作用于宽高,这将导致缩放后的图片大小以采样率的2次方形式递减,即缩放为1/(inSanpleSize的2次方),比如inSampleSize为4,那么缩放比例就是1/16.有一种特殊情况,那就是inSampleSize的取值应该总是2的指数,比如1,2,4,8,16,等等。如果外界传递给系统的inSampleSize不为2的指数,那么系统会向下取整并选择一个最接近2的指数替代,比如3,系统会选择2来替代,但是经过验证发现这个结论并非在所有的Android版本上都成立,因此把它当成一个开发建议即可。

考虑以下实际情况,比如ImageView的大小是100100像素,而图片的原始大小为200200,那么选择采样率inSampleSize为2即可。但是如果图片大小为200300呢?
这个时候采样率还应该选择2,这样缩放后的图片大小为100
150像素,任然是适合ImageView的,如果采样率是3,那么缩放后的图片大小就会小于ImageView所期望的大小,这样图片就会被拉伸从而导致模糊。

通过采样率就可有效的加载图片,那么如何获取采样率呢?

获取采样率的流程如下

(1)将BitmapFactory.Option的inJustDecodeBounds参数设为true并加载图片
(2)从BitmapFactory.Option中取出图片的原始宽高,它们对应于outWidth和outHeight参数
(3) 根据采样率的规则并结合目标View的所需大小计算出采样率inSampleSize。
(4)将BitmapFactory.Option的inJustDecodeBounds参数设为false,然后重新加载图片。


经过上面4个步骤,加载出的图片就是最终缩放的图片,当然也有可能不需要缩放。这里说明一下inJustDecodeBounds参数,当此参数为true时,BitmapFactory只会解析图片的原始宽高信息,并不会去真正去加载图片,所以这个操作是轻量级的。另外需要注意的是,这个时候的BitmapFactory获取图片的宽高信息和图片的位置以及程序运行的设备有关,比如同一张图片放在不同的drawable目录下或者程序运行在不同的屏幕密度设备上,这都可能导致BitmapFactory获取不同的结果,之所以会出现这样的现象,这个和Android上资源加载机制有关,相信读者在平日里肯定有所体会,这里就详细说明了。

代码实现

public static Bitmap decodeSampleFromResources(Resource res,int resId,int reqWidth,int reqHeight){

final BitmapFactory.Options options=new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeResource(res,resId,options);

options.inJustDecodeBoundles=false;
return BitmapFactory.decodeResource(res,resId,options);
}

public static int calculateSampleSize{

final int height=options.outHeight;
final int width=options.outWidth;
int inSampleSize=1;

if(height>resHeight||width>resWidth){
final int halfHeight=height/2;
final int halfwidth=width/2;

while((halfHeight/inSampleSize)>=reqHeigh&& (halfWidth/inSampleSize)>=reqWidth){
inSampleSize*=2;
}
}
return inSampleSize;
} 

有了上面的两个方法,实际使用就很简单了,比如ImageView所期望的图片大小为100*100像素,这个时候就可以通过如下方式高效地加载并显示图片:

mImageView.setImageBitmap{
decodeSampleBitmapFromResource(getResources(),R.id.image,100,100);

除了BitmapFactory的decodeResource方法,其他三个decode系列的方法也是支持采样加载的,并且处理方式也是类似的。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
android Bitmap用法总结 Bitmap用法总结 1、Drawable → Bitmap public static Bitmap drawableToBitmap(Drawable drawable) { Bitmap bitmap = Bitmap .createBitmap( drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); // canvas.setBitmap(bitmap); drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight()); drawable.draw(canvas); return bitmap; } 2、从资源中获取Bitmap Resources res=getResources(); Bitmap bmp=BitmapFactory.decodeResource(res, R.drawable.pic); 3、Bitmap → byte[] private byte[] Bitmap2Bytes(Bitmap bm){ ByteArrayOutputStream baos = new ByteArrayOutputStream(); bm.compress(Bitmap.CompressFormat.PNG, 100, baos); return baos.toByteArray(); } 4、byte[] → Bitmap private Bitmap Bytes2Bimap(byte[] b){ if(b.length!=0){ return BitmapFactory.decodeByteArray(b, 0, b.length); } else { return null; } } 5、保存bitmap static boolean saveBitmap2file(Bitmap bmp,String filename){ CompressFormat format= Bitmap.CompressFormat.JPEG; int quality = 100; OutputStream stream = null; try { stream = new FileOutputStream("/sdcard/" + filename); } catch (FileNotFoundException e) { // TODO Auto-generated catch block Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. e.printStackTrace(); } return bmp.compress(format, quality, stream); } 6、将图片按自己的要求缩放 // 图片源 Bitmap bm = BitmapFactory.decodeStream(getResources() .openRawResource(R.drawable.dog)); // 获得图片的宽高 int width = bm.getWidth(); int height = bm.getHeight(); // 设置想要的大小 int newWidth = 320; int newHeight = 480; // 计算缩放比例 float scaleWidth = ((float) newWidth) / width; float scaleHeight = ((float) newHeight) / height; // 取得想要缩放的matrix参数 Matrix matrix = new Matrix(); matrix.postScale(scaleWidth, scaleHeight); // 得到新的图片 Bitmap newbm = Bitmap.createBitmap(bm, 0, 0, width, height, matrix, true); // 放在画布上 canvas.drawBitmap(newbm, 0, 0, paint); 相关知识链接:http://www.eoeandroid.com/thread-3162-1-1.html 7、bitmap的用法小结 BitmapFactory.Options option = new BitmapFactory.Options(); option.inSampleSize = 2; //将图片设为原来宽高的1/2,防止内存溢出 Bitmap bm = BitmapFactory.decodeFile("",option);//文件流 URL url = new URL(""); InputStream is = url.openStream(); Bitmap bm = BitmapFactory.decodeStream(is); android:scaleType: android:scaleType是控制图片如何resized/moved来匹对ImageView的size。ImageView.ScaleType / android:scaleType值的意义区别: CENTER /center 按图片的原来size居中显示,当图片长/宽超过View的长/宽,则截取图片的居中部分 显示 CENTER_CROP / centerCrop 按比例扩大图片的size居中显示,使得图片长(宽)等于或大于View的长 (宽) CENTER_INSIDE / centerInside 将图片的内容完整居中显示,通过按比例缩小或原来的size使得图片 长/宽等于或小于View的长/宽 Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. FIT_CENTER / fitCenter 把图片按比例扩大/缩小到View的宽度,居中显示 FIT_END / fitEnd 把图片按比例扩大/缩小到View的宽度,显示在View的下部分位置 FIT_START / fitStart 把图片按比例扩大/缩小到View的宽度,显示在View的上部分位置 FIT_XY / fitXY 把图片 不按比例 扩大/缩小到View的大小显示 MATRIX / matrix 用矩阵来绘制,动态缩小放大图片来显示。 //放大缩小图片 public static Bitmap zoomBitmap(Bitmap bitmap,int w,int h){ int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); float scaleWidht = ((float)w / width); float scaleHeight = ((float)h / height); matrix.postScale(scaleWidht, scaleHeight); Bitmap newbmp = Bitmap.createBitmap(bitmap, 0, 0, width, height, matrix, true); return newbmp; } //将Drawable转化为Bitmap public static Bitmap drawableToBitmap(Drawable drawable){ int width = drawable.getIntrinsicWidth(); int height = drawable.getIntrinsicHeight(); Bitmap bitmap = Bitmap.createBitmap(width, height, drawable.getOpacity() != PixelFormat.OPAQUE ? Bitmap.Config.ARGB_8888 : Bitmap.Config.RGB_565); Canvas canvas = new Canvas(bitmap); drawable.setBounds(0,0,width,height); drawable.draw(canvas); return bitmap; Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. } //获得圆角图片的方法 public static Bitmap getRoundedCornerBitmap(Bitmap bitmap,float roundPx){ Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap .getHeight(), Config.ARGB_8888); Canvas canvas = new Canvas(output); final int color = 0xff424242; final Paint paint = new Paint(); final Rect rect = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight()); final RectF rectF = new RectF(rect); paint.setAntiAlias(true); canvas.drawARGB(0, 0, 0, 0); paint.setColor(color); canvas.drawRoundRect(rectF, roundPx, roundPx, paint); paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN)); canvas.drawBitmap(bitmap, rect, rect, paint); return output; } //获得带倒影的图片方法 public static Bitmap createReflectionImageWithOrigin(Bitmap bitmap){ final int reflectionGap = 4; int width = bitmap.getWidth(); int height = bitmap.getHeight(); Matrix matrix = new Matrix(); matrix.preScale(1, -1); Bitmap reflectionImage = Bitmap.createBitmap(bitmap, 0, height/2, width, height/2, matrix, false); Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height/2), Config.ARGB_8888); Canvas canvas = new Canvas(bitmapWithReflection); canvas.drawBitmap(bitmap, 0, 0, null); Paint deafalutPaint = new Paint(); Generated by Foxit PDF Creator © Foxit Software http://www.foxitsoftware.com For evaluation only. canvas.drawRect(0, height,width,height + reflectionGap, deafalutPaint); canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null); Paint paint = new Paint(); LinearGradient shader = new LinearGradient(0, bitmap.getHeight(), 0, bitmapWithReflection.getHeight() + reflectionGap, 0x70ffffff, 0x00ffffff, TileMode.CLAMP); paint.setShader(shader); // Set the Transfer mode to be porter duff and destination in paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN)); // Draw a rectangle using the paint with our linear gradient canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint); return bitmapWithReflection; } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值