高效加载大的图片

Loading Large Bitmaps Efficiently 高效的加载大图

图片可能以各种各样的形状,尺寸展现出来,大多数情况下它们的展现方式,是超出一般应用程序的UI需求的。举例来说,系统的”相册”应用中展现的图片(Android设备拍摄的相片)是超过你的设备屏幕的分辨率的。

假设你在一个内存有限的环境下工作,理想情况下你只需要加载一个低分辨率的版本进入内存。相对较低的分辨率的图片应该匹配UI组件的需求,并且将它们显示出来。 在这种情况下(需求的分辨率不需要很高),一张高分辨率的图片不会在视觉上给人更好的感受,因为你显示的效果不需要那么好。但是这张图片会占用很多内存,当你在快速缩放的时候它会给视觉显示任务带来负担。

这篇文章将教会你 在解析高分辨率图片的时候 通过 处理将其变为相对低一点的分辨率的图片然后加载进内存,这样就不会超出每个应用程序的内存限制了。

Read Bitmap Dimensions and Type 获取图片的尺寸和类型

BitmapFactory这个类提供了几种解析图片的方法 (decodeByteArray(), decodeFile(), decodeResource() )用来从不同的数据源创建一个Bitmap对象。 基于你的数据源,来选择合适的方法。

这些方法在执行过程中会为已经创建的bitmap分配内存,所以会很容易的出现OOM(内存溢出)异常。

每一个方法都会自己的特征让你来区分它们。通过 BitmapFactory.Options 类。在解析的过程中,设置inJustDecodeBounds属性 为 true,通过返回值 null 就可以避免内存分配。然后设置outWidth,outHeight,outMimeType.通过这种方式你就可以在图片创建(内存分配)之前读取图片的尺寸和类型。

下面是代码示例

BitmapFactory.Options options = new BitmapFactory.Options();

options.inJustDecodeBounds = true;
//设置为true,解析图片的时候返回值是null,就可以不用给图片分配内存,但是这个时候你是可以得到图片的宽高的
//得到信息之后记得 还原为false,否则解析的bitmap为null
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

为了避免OOM异常,你必须在加载和解析图片的时候检查一下尺寸,除非你很清楚的知道你要加载的图片是在合理的内存范围之内的。

Load a Scaled Down Version into Memory 加载一个缩放的图片进入内存

现在我们已经知道了图片的尺寸,它们可以用来加载(或者缩放之后)进入内存了,以下这些因素是你要考虑的。

  • 当你加载一整张图片进入内存的时候,你必须估算一下内存的消耗。
  • 包括了应用的其他内存消耗之后,你可以给你当前要加载的图片分配多少内存。
  • 用来显示图片的 目标控件(ImageView,或者UI组件)的尺寸。
  • 当前设备的屏幕尺寸和分辨率。

举例来说,当你要 将一个1024x768像素的图片显示在 128x96像素的ImageView上的时候是非常浪费的。

在 BitmapFactory.Options 对象中设置 inSampleSize 为 true,就可以在解析图片的时候,加载一个缩小的图片进入内存。 举例来说 如果你把 inSampleSize的值设置为4的话,一张 2048x1536分辨率的图片就可以被处理为 大约512x384分辨率的bitmap。那么原先一张加载入内存需要12M的图片就仅仅用了0.75M(假设bitmap是ARGB_8888).

下面有一个方法用来计算 缩放比例的值,根据你的图片尺寸和要求的尺寸。

public static int calculateInSampleSize(

        BitmapFactory.Options options, int reqWidth, int reqHeight) {
// Raw height and width of image
final int height = options.outHeight;
final int width = options.outWidth;
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.
    //如果图片缩小2倍之后任然大于需求尺寸,则继续缩放。  这样循环下来,inSampleSize就是2的次方
    while ((halfHeight / inSampleSize) > reqHeight
            && (halfWidth / inSampleSize) > reqWidth) {
        inSampleSize *= 2;
    }
  }

  return inSampleSize;
}

注意:Note: A power of two value is calculated because the decoder uses a final value by rounding down to the nearest power of two, as per the inSampleSize documentation.

这个值的计算是用2的幂来计算的,因为decode的时候一般会对最终结果进行向下取近似值(如果不是整数)。

用下面这个方法解析一张图片的时候 首先将 inJustDecodeBounds 设置为 true,计算出inSampleSize并且设置options的inSampleSize 然后将inJustDecodeBounds 设置为false;

public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
    int reqWidth, int reqHeight) {

// First decode with inJustDecodeBounds=true to check dimensions
//第一次 先将 inJustDecodeBounds=true  查看尺寸,计算inSampleSize的值。
final BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(res, resId, options);

// Calculate inSampleSize
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);

//计算完了之后 设置为 false
// Decode bitmap with inSampleSize set
options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);

}

下面这个方法可以很简单的让一张超过ImageView尺寸的图片以缩略图的形式显示出来。

mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));

你也可以模仿这个过程,定制一些 别的方法 BitmapFactory.decode*。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值