Displaying Bitmaps Efficiently (二)

高效地加载图片

——Android官网原文翻译

图片拥有不同的形状和尺寸。在很多时候,图片的实际尺寸要比UI中需要的尺寸大。例如,系统的Gallery应用所显示的那些由设备的照相机照出的图片的分辨率要远高于屏幕的分辨率。

考虑到您的应用程序要工作在有限的内存,理想状态下,您只需要加载一张低分辨率版本的图片到内存,而分辨率的大小应该和显示这张图片的UI组件的大小相吻合。加载一张分辨率很高的图片并不能为用户体验带来什么好处,但这么做却占用了昂贵的内存空间,并且将导致应用程序性能受到影响。

这一节将会向您讲述,相对于一张大图片,怎样加载它的小尺寸版本,以此避免超出应用程序的内存限制。

1.读取图片的大小和类型

BitmapFactory类提供了一系列解码图片的方法(decodeByteArray() , decodeFile() , decodeResource(),等等)用于从不同的源创建Bitmap对象。根据您的图片的数据来源挑选最恰当的解码方法。这些方法尝试为创建的Bitmap对象分配内存,因此很容易造成OutOfMemory的异常。每种解码的方法都有一个附加的参数,让您使用BitmapFactory.Options指定一些解码图片的选项。将inJustDecodeBounds属性设置为true会使解码图片时不分配内存,而返回一个Bitmap对象的空引用,但传入的Options参数中的outWidth,outHeight和outMimeType却被附了值。这个技术使您可以在不构造(也不分配内存空间)Bitmap对象的情况下能够获知目标图片的尺寸和类型。

BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;

为了避免java.lang.OutOfMemory 异常,除非您能够确保图片来源会提供给您尺寸非常合适的图片,否则请在解码图片之前检查这张图片的尺寸。

2.加载一张按比例缩小的图片到内存

现在,图片的实际尺寸已经知道了,现在我们可以决定要加载原始大小的图片,还是要加载一张尺寸更小的图片。我们可以基于以下因素需要来考虑:

  1. 估计加载原始尺寸的图片到内存的情况下内存的占用率
  2. 应用程序为加载此图片所需要的内存空间大小
  3. 图片要显示的ImageView或者其他目标UI组件的大小
  4. 当前设备的屏幕尺寸和分辨率

举例来说,如果一张图片在将要在目标ImageView上显示的大小是128x96像素,那么我们显然没有必要加载一张1024x768像素的图片。

解决的办法是,告诉decoder加载一张小尺寸的图片到内存,把BitmapFactory.Options对象中的inSampleSize属性设置为true。举例来说,一张实际分辨率为2048x1536的图片,把inSampleSize设置为4时会产生一张约512x384的Bitmap。把这张小尺寸的图片加载进内存只需0.75MB的内存,而原始尺寸的图片需要占用12MB的内存(假设图片使用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) {

        // Calculate ratios of height and width to requested height and width
        final int heightRatio = Math.round((float) height / (float) reqHeight);
        final int widthRatio = Math.round((float) width / (float) reqWidth);

        // Choose the smallest ratio as inSampleSize value, this will guarantee
        // a final image with both dimensions larger than or equal to the
        // requested height and width.
        inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio;
    }

    return inSampleSize;
}

注意:使用2作为inSampleSize的值通常来说会更快更有效率。但是,如果您计划在内存或者硬盘中缓存某个尺寸的图片的话,那么还是使用最合适的大小解码该图片。

使用该方法,首先把inJustDecodeBounds设置为true,将该options对象传进方法,调用过该方法后,options被重新设置值。然后再此调用decode方法,使用新的inSampleSize值,并把inJustDecodeBounds设置为false。

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

    // First decode with inJustDecodeBounds=true to check dimensions
    final BitmapFactory.Options options = new BitmapFactory.Options();
    options.inJustDecodeBounds = true;
    BitmapFactory.decodeResource(res, resId, options);

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

    // Decode bitmap with inSampleSize set
    options.inJustDecodeBounds = false;
    return BitmapFactory.decodeResource(res, resId, options);
}

这个方法将加载一张小尺寸图片来代替原始图片的操作变得很简单,就像如下代码:

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

您也可以使用这种方式来加载其他数据源类型的图片。

转载于:https://www.cnblogs.com/xp880906/p/3312743.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值