本文主要是介绍高效地显示Bitmap图片,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
- 图片有不同的形状与大小。在大多数情况下它们的实际大小都比需要呈现出来的要大很多。例如,系统的Gallery程序会显示那些你使用设备camera拍摄的图片,但是那些图片的分辨率通常都比你的设备屏幕分辨率要高很多。
- 考虑到程序是在有限的内存下工作,理想情况是你只需要在内存中加载一个低分辨率的版本即可。这个低分辨率的版本应该是与你的UI大小所匹配的,这样才便于显示。一个高分辨率的图片不会提供任何可见的好处,却会占用宝贵的(precious)的内存资源,并且会在快速滑动图片时导致(incurs)附加的效率问。
- 这一课会介绍如何通过加载一个低版本的图片到内存中去decoding大的bitmaps,从而避免超出程序的内存限制。
Read Bitmap Dimensions and Type [读取位图的尺寸与类型]
BitmapFactory
类提供了一些decode的方法 (decodeByteArray()
,decodeFile()
,decodeResource()
, etc.) 用来从不同的资源中创建一个Bitmap
. 根据你的图片数据源来选择合适的decode方法. 那些方法在构造位图的时候会尝试分配内存,因此会容易导致OutOfMemory
的异常。每一种decode方法都提供了通过BitmapFactory.Options
来设置一些附加的标记来指定decode的选项。设置inJustDecodeBounds
属性为true可以在decoding的时候避免内存的分配,它会返回一个null的bitmap,但是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
的异常,我们在真正decode图片之前检查它的尺寸,除非你确定这个数据源提供了准确无误的图片且不会导致占用过多的内存。
Load a Scaled Down Version into Memory [加载一个按比例缩小的版本到内存中]
- 通过上面的步骤我们已经知道了图片的尺寸,那些数据可以用来决定是应该加载整个图片到内存中还是加一个缩小的版本。下面有一些因素需要考虑:
- 评估加载完整图片所需要耗费的内存。
- 程序在加载这张图片时会涉及到其他内存需求。
- 呈现这张图片的组件的尺寸大小。
- 屏幕大小与当前设备的屏幕密度。
- 例如,如果把一个原图是1024*768 pixel的图片显示到ImageView为128*96 pixel的缩略图就没有必要把整张图片都加载到内存中。
- 为了告诉decoder去加载一个低版本的图片到内存,需要在你的BitmapFactory.Options 中设置
inSampleSize
为true 。
For example, 一个分辨率为2048x1536 的图片,如果设置inSampleSize
为4,那么会产出一个大概为512x384的bitmap。加载这张小的图片仅仅使用大概0.75MB,如果是加载全图那么大概要花费12MB(前提都是bitmap的配置是ARGB_8888
). 下面有一段根据目标图片大小来计算Sample图片大小的Sample Code:
- 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) {
- if (width > height) {
- inSampleSize = Math.round((float)height / (float)reqHeight);
- } else {
- inSampleSize = Math.round((float)width / (float)reqWidth);
- }
- }
- return inSampleSize;
- }
- Note: 设置
inSampleSize
为2的幂对于decoder会更加的有效率,然而,如果你打算把调整过大小的图片Cache到磁盘上,设置为更加接近的合适大小则能够更加有效的节省缓存的空间. - 为了使用这个方法,首先需要设置
inJustDecodeBounds
为true
, 把options的值传递过来,然后使用inSampleSize
的值并设置inJustDecodeBounds
为false 来重新Decode一遍。
- 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);
- }
- 使用上面这个方法可以简单的加载一个任意大小的图片并显示为100*100 pixel的缩略图形式。像下面演示的一样:
- mImageView.setImageBitmap(
- decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
- 你可以通过替换合适的
BitmapFactory.decode*
方法来写一个类似的方法从其他的数据源进行decode bitmap。
-
public
Bitmap inBitmap
If set, decode methods that take the Options object will attempt to reuse this bitmap when loading content.
public int
inDensity
The pixel density to use for the bitmap.
public boolean
inDither
If dither is true, the decoder will attempt to dither the decoded image.
public boolean
inInputShareable
This field works in conjuction with inPurgeable.
public boolean
inJustDecodeBounds
If set to true, the decoder will return null (no bitmap), but the out…
public boolean
inMutable
If set, decode methods will always return a mutable Bitmap instead of an immutable one.
public boolean
inPreferQualityOverSpeed
If inPreferQualityOverSpeed
is set to true, the decoder will try to decode the reconstructed image to a higher quality even at the expense of the decoding speed. publicBitmap.Config
inPreferredConfig
If this is non-null, the decoder will try to decode into this internal configuration.
public boolean
inPurgeable
If this is set to true, then the resulting bitmap will allocate its pixels such that they can be purged if the system needs to reclaim memory.
public int
inSampleSize
If set to a value > 1, requests the decoder to subsample the original image, returning a smaller image to save memory.
public boolean
inScaled
When this flag is set, if
inDensity and inTargetDensity are not 0, the bitmap will be scaled to match inTargetDensity when loaded, rather than relying on the graphics system scaling it each time it is drawn to a Canvas. public int
inScreenDensity
The pixel density of the actual screen that is being used.
public int
inTargetDensity
The pixel density of the destination this bitmap will be drawn to.
public byte[]
inTempStorage
Temp storage to use for decoding.
public boolean
mCancel
Flag to indicate that cancel has been called on this object.
public int
outHeight
The resulting height of the bitmap, set independent of the state of inJustDecodeBounds.
public
String outMimeType
If known, this string is set to the mimetype of the decoded image.
public int
outWidth
The resulting width of the bitmap, set independent of the state of inJustDecodeBounds.
这个表格是从android sdk文档里摘出来的,简单看一下说明就明白是什么意思了。
下面我们回到我们的主题上来:怎样获取图片的大小?
思路很简单:
首先我们把这个图片转成Bitmap,然后再利用Bitmap的getWidth()和getHeight()方法就可以取到图片的宽高了。
新问题又来了,在通过BitmapFactory.decodeFile(String path)方法将突破转成Bitmap时,遇到大一些的图片,我们经常会遇到OOM(Out Of Memory)的问题。怎么避免它呢?
这就用到了我们上面提到的BitmapFactory.Options这个类。BitmapFactory.Options这个类,有一个字段叫做
inJustDecodeBounds 。SDK中对这个成员的说明是这样的:
If set to true, the decoder will return null (no bitmap), but the out…
也就是说,如果我们把它设为true,那么BitmapFactory.decodeFile(String path, Options opt)并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM了。
示例代码如下:- BitmapFactory.Options options = new BitmapFactory.Options();
- options.inJustDecodeBounds = true;
- Bitmap bmp = BitmapFactory.decodeFile(path, options);
复制代码
这段代码之后,options.outWidth 和 options.outHeight就是我们想要的宽和高了。
有了宽,高的信息,我们怎样在图片不变形的情况下获取到图片指定大小的缩略图呢?
比如我们需要在图片不变形的前提下得到宽度为200的缩略图。
那么我们需要先计算一下缩放之后,图片的高度是多少- int height = options.outHeight * 200 / options.outWidth;
- options.outWidth = 200;
- options.outHeight = height;
- options.inJustDecodeBounds = false;
- Bitmap bmp = BitmapFactory.decodeFile(path, options);
- image.setImageBitmap(bmp);
复制代码
这样虽然我们可以得到我们期望大小的ImageView
但是在执行BitmapFactory.decodeFile(path, options);时,并没有节约内存。要想节约内存,还需要用到BitmapFactory.Options这个类里的inSampleSize 这个成员变量。
我们可以根据图片实际的宽高和我们期望的宽高来计算得到这个值。- inSampleSize = options.outWidth / 200;
另外,为了节约内存我们还可以使用下面的几个字段:
- options.inPreferredConfig = Bitmap.Config.ARGB_4444;
// 默认是Bitmap.Config.ARGB_8888 - options.inPurgeable = true;
- options.inInputShareable = true;
这篇关于高效地显示Bitmap图片的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!