您的位置:首页 > 职场人生

设置图片inSampleSize但是内存没变?或许你应该看看这个

2017-11-13 17:27 281 查看
对于安卓开发来说,内存溢出(oom)是安卓程序员不可绕过的坎,特别是对于大图片来说,加载时候的大内存更是常常让人胆战心惊。

很多安卓程序员都知道,避免图片加载大内存的最常用方法,那就是用BitmapFactory的options,设置这个options的inSampleSize来达到将图片按照实际显示大小去缩小自己图片,来达到减少内存。

若是你去百度inSampleSize,那么你会看到最多的用法是这样的:

private Bitmap getimage(String srcPath) {
BitmapFactory.Options newOpts = new BitmapFactory.Options();
//开始读入图片,此时把options.inJustDecodeBounds 设回true了
newOpts.inJustDecodeBounds = true;
Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空

newOpts.inJustDecodeBounds = false;
int w = newOpts.outWidth;
int h = newOpts.outHeight;
//现在主流手机比较多是800*480分辨率,所以高和宽我们设置为
float hh = 800f;//这里设置高度为800f
float ww = 480f;//这里设置宽度为480f
//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可
int be = 1;//be=1表示不缩放
if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放
be = (int) (newOpts.outWidth / ww);
} else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放
be = (int) (newOpts.outHeight / hh);
}
if (be <= 0)
be = 1;
newOpts.inSampleSize = be;//设置缩放比例
//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了
bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
return compressImage(bitmap);//压缩好比例大小后再进行质量压缩
}


看起来好像完全没有问题,根据自己想要的的图片大小,然后去计算将原图缩放的inSampleSize,然后赋值给bitmap以达到缩放图片大小。

很多人看到这样算法便喜不自胜急忙将它复制到自己工程,可是复制进去之后他们却傻眼了,内存跟以前没用时候差不多啊。

为什么会这样?

因为,inSampleSize只能为整数啊。

我们举个例子,假如有一张高度为700的图片,你想把它缩放为500,你要是用上面那个算法去算,你会发现,你算出来的inSampleSize其实为1!!!

而1,那就是图片不变,没有压缩

在这里说一下,inSampleSize的值只能为2的倍数,1,4,8,16……

1的话是不变,2的话是图片变为原来4分之一大小,8是变为原来64分之一大小,以此类推

所以到了最后,你会发现你想要压缩的图片其实根本没有被压缩。

好吧,就算有变,但其实也很难,甚至几乎不可能变到你想要的图片大小,就比如上面说的那个例子,无论你设置inSampleSize为什么值,你都会发现,你最终得到的图片要么太大要么太小。

但是安卓怎么可能那么垃圾,

对的,安卓才不会这么垃圾呢,

其实与inSampleSize相关的还有几个参数,比如inScaled,inTargetDensity,inDensity。

这些参数其实非常有用,与inSampleSize一起用才会完美。

其中:

inScaled:将它设置为true,那么代表这张图片可以缩放

inDensity:图片的原来密度,默认一般为160.

inTargetDensity:图片的目标密度,图片操作之后的密度

其实你设置一张图片的BitmapFactory的options,这张图片的输出宽高是这样决定的:

输出图片的宽高= (原图片的宽高 * (inTargetDensity / inDensity)) / inSampleSize


所以你要是现将一张700的图片缩小为600,你可以计算一下图片的inTargetDensity,如下面方法

/**
* 获取特定大小缩略图
* @param imageid 图片资源id
* @param size 你想要获取的图片大小尺寸
* @return
*/
public Bitmap getSampleBitmap2(int imageid,int size){
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeResource(getResources(),imageid,options);
options.inPreferredConfig = Bitmap.Config.ARGB_8888;
options.inSampleSize= calculateInSampleSize(options,size);
//设置图片可以缩小
options.inScaled = true;
int calsize=options.outHeight>options.outWidth?options.outWidth:options.outHeight;
/**
* 计算图片缩小的目标密度,在这里说一下,有一条公式:
* 输出图片的宽高= (原图片的宽高 * (inTargetDensity / inDensity)) / inSampleSize
* 一般来说,图片的options.inDensity默认为160
* 所以inTargetDensity计算公式为:(希望输出的宽高*options.inDensity)/(原来图片的宽高/options.inSampleSize)
*/
options.inTargetDensity =(size*options.inDensity)/(calsize/options.inSampleSize);
options.inJustDecodeBounds = false;
Bitmap bitmap = BitmapFactory.decodeResource(getResources(),imageid,options);
return bitmap;
}


//谷歌源码里面的计算simplesize方法,
public int calculateInSampleSize(BitmapFactory.Options options,
int size) {
int reqWidth,reqHeight;
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
if(options.outHeight>options.outWidth){
reqWidth=size;
reqHeight=size*options.outHeight/options.outWidth;
}else{
reqWidth=size*options.outWidth/options.outHeight;
reqHeight=size;
}
int inSampleSize = 1;

if (height > reqHeight || width > reqWidth) {

final int halfHeight = height / 2;
final int halfWidth = width / 2;

// Calculate the largest inSampleSize value that is a power of 2 and
// keeps both
// height and width larger than the requested height and width.
while ((halfHeight / inSampleSize) > reqHeight
&& (halfWidth / inSampleSize) > reqWidth) {
inSampleSize *= 2;
}
}

return inSampleSize;
}


比如我将一张宽700图片缩小为宽450的图片,在没设置inTargetDensity的时候





计算出来的inSampleSize为1,图片尺寸很大,没有变

所占用内存:



差不多40M

而在设置了inTargetDensity 之后





计算出来的inSampleSize仍然为1,但是图片尺寸却变为我想要获取的大小450了!!!

而所占用的内存:



只有14M左右!!!!!!!!!!

40?14!是不是感觉世界瞬间美好很多啦~

对了,后面顺便说一下,若你将根据这个方法得到的图片赋值给另一个bitmap的时候,可能会出现,这个被赋值的bitmap图片虽然图片大小不变,但是显示变了,那是因为这个inTargetDensity 属性不会自动赋值给新的bitmap,可以这样做

//创建一张新的bitmap,跟传入图片一样宽的正方形bitmap,
Bitmap b=Bitmap.createBitmap(bitmapSize,bitmapSize, Bitmap.Config.ARGB_8888);
//将图片密度修改为上面那张的图片密度
b.setDensity(bitmap.getDensity());


这样新得到的b图片就会跟原来那张一样了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
相关文章推荐