Android中图片的读取,修改,显示和保存涉及到的类大致如图所示。

在读取图片文件时,先将图片文件转换为InputStream对象,然后通过BitmapFactory将其转换为Bitmap对象。
在图片保存时,先将Bitmap对象转换为OutputStream对象,然后再将OutputStream输出到文件中。
如果要对图片进行修改,可以通过将Bitmap对象转换为颜色数组(int[])来修改,也可以通过Canvas来修改。此外Bitmap类提供了一个createBitmap的静态方法,可以对Bitmap对象做一些转换。
显示图片时,可以将Bitmap对象转换为Drawable对象,然后设置给ImageView。
本文介绍图片文件的读取。

Android支持的图片格式

目前在Android中支持的图片文件格式如下。其中WebP格式是从Android4.0开始支持,但在Android4.0到Android4.2.1之间的Android系统不支持无损压缩和有透明度的WebP格式图片,Android4.2.1之后才开始支持所有的WebP格式图片。
下图截取自http://developer.android.com/guide/appendix/media-formats.html

读取图片文件到InputStream

1、读取SD卡中的图片到InputStream
对SD卡中的文件,获取到文件的路径之后便可以通过FileInputStream类来生成InputStream。FileInputStream类是InputStream类的派生类。代码示例如下。

FileInputStream stream = new FileInputStream(fileName);

2、读取resource中的图片到InputStream
对resource/drawable目录下的图片,每个文件都会在R文件中生成一个对应的整数id,通过“R.drawable.文件名”来得到id值,然后通过Resource类的openRawResource()来得到该文件对应的InputStream对象。代码示例如下。

InputStream strem = getResources().openRawResource(R.drawable.name);

3、读取assets中的图片到InputStream
对assets目录中的图片,先通过getAssets()得到AssetManager对象,然后通过AssetManager对象的open方法来打开文件,得到该文件对应的InputStream对象。代码示例如下。

InputStream strem = getAssets().open("apple.png");

注意:如果图片在assets根目录,读取时只需要加图片文件名,不需要加/assets/,图片文件名需要包含扩展名。如果图片在assets的子目录中,需要加上子目录的路径,例如apple.png在/assert/fruit/spring/目录中,则open参数为”/fruit/spring/apple.png”。

4、 通过Uri读取图片到InputStream
Uri全写是Universal Resource Identifier(通用资源标志符),Android系统中所有资源都可以用Uri来表示。在获取到资源的Uri后可以通过ContentResolver将其解析成InputStream对象。代码示例如下。

InputStream strem = getContentResolver().openInputStream(uri);

openInputStream()核心代码如下。

String scheme = uri.getScheme();if (SCHEME_ANDROID_RESOURCE.equals(scheme)) {    OpenResourceIdResult r = getResourceId(uri);    InputStream stream = r.r.openRawResource(r.id);    return stream;} else if (SCHEME_FILE.equals(scheme)) {    return new FileInputStream(uri.getPath());} else {    AssetFileDescriptor fd = openAssetFileDescriptor(uri, "r", null);    return fd.createInputStream();}

这里openInputStream()内先判断Uri的scheme类型,如果是resource类型,则先调用getResourceId()得到该Uri对应的资源id,然后调用openRawResource()读取文件到InputStream对象。如果是文件类型,则调用uri.getPath()得到文件路径,然后调用FileInputStream()读取文件到InputStream对象。对其他类型(即assets类型)则调用openAssetFileDescriptor()先得到一个AssetFileDescriptor对象,然后再得到一个InputStream对象。
可以看到,通过Uri读取图片的流程和上述根据类型直接读取大体是一样的(assets类型稍有不同)。

将InputStream转换为Bitmap

通过BitmapFactory的decodeStream()方法可以将InputStream转换为Bitmap对象。代码示例如下。

Bitmap bitmap = BitmapFactory.decodeStream(stream);

直接读取图片文件到Bitmap

上述流程是先将图片文件读取到InputStream中,然后将InputStream转换为Bitmap对象,需要两步。BitmapFactory提供了一些静态类可以直接将图片文件转换为Bitmap对象。
直接读取SD卡中的图片到Bitmap示例代码如下。

Bitmap bitmap = BitmapFactory.decodeFile(fileName);

直接读取resource中的图片到Bitmap示例代码如下。

Bitmap bitmap = BitmapFactory.decodeResource(getResources(), id);

BitmapFactory并没有提供直接读取assets中图片的api。
查看BitmapFactory源码可以看到,BitmapFactory.decodeFile()和BitmapFactory.decodeResource()两个api都是先将SD卡或resource中的图片读取到InputStream,然后再调用BitmapFactory.decodeStream()来解析。由此可以看出,直接读取图片到Bitmap和先读取图片到InputStream,然后将InputStream转换为Bitmap本质上是一样的,只是BitmapFactory将这两步封装在一起。因此,如果要将非assets中的图片文件转换为Bitmap,一般就直接使用BitmapFactory即可。

BitmapFactory.Options

BitmapFactory在decode时可以传递一个Options参数,用来设置转换到Bitmap时的一些参数。
BitmapFactory.Options中包含了一系列的public成员变量,每个成员变量代表一个转换参数。这里列出了BitmapFactory.Options类中定义的全部public成员变量。在BitmapFactory.Options类的注释中详细的描述了每个成员变量的含义。这里只介绍几个常用的成员变量的用法。

    public static class Options {        public Bitmap inBitmap;        public boolean inMutable;        public boolean inJustDecodeBounds;        public int inSampleSize;        public Bitmap.Config inPreferredConfig = Bitmap.Config.ARGB_8888;        public boolean inPremultiplied;        public boolean inDither;        public int inDensity;        public int inTargetDensity;        public int inScreenDensity;        public boolean inScaled;        public boolean inPurgeable;        public boolean inInputShareable;        public boolean inPreferQualityOverSpeed;        public int outWidth;        public int outHeight;        public String outMimeType;        public byte[] inTempStorage;        public boolean mCancel;    }

inJustDecodeBounds:此变量默认为false。当此变量被设置为true时,表示只对图片的Bound(图片的范围,也就是长宽包含的像素大小)进行解析,不生成Bitmap对象。在BitmapFactory.Options定义中可以看到有三个out开头的成员变量,outWidth,outHeight和outMimeType。当inJustDecodeBounds设置为true时,会分别将图片的宽度,高度和Mime类型保存到这三个成员变量中。
inSampleSize:inSampleSize是一个整数,表示对图片像素的取样比例,即图片中每inSampleSize个像素取样后对应Bitmap中的一个像素。例如inSampleSize=2,表示图片中每两个像素对应Bitmap中的一个像素,转换后Bitmap的宽和高包含的像素个数是原图片的1/2,总像素个数是原图的1/4。inSampleSize=4,表示图片中每四个像素对应Bitmap中的一个像素,转换后Bitmap的宽和高包含的像素个数是原图片的1/4,总像素个数是原图的1/16。如果它小于1等于1,则表示完全取样,即图片中每个像素都会被提取保存到Bitmap中,转换后Bitmap的宽和高包含的像素个数和原图片相等。图片取样时只能按照2的整数次幂来取样,比如1,2,4,8,16……,如果inSampleSize不是2的整数次幂,则实际取样时使用和inSampleSize最接近的2的整数次幂来取样,比如inSampleSize= 10,和10最接近的2的整数次幂是8,因此会每8个像素取样一次。
inPreferredConfig:表示图片中每个像素的保存方式。默认为Bitmap.Config.ARGB_8888,表示每个像素需要包含Alpha,R,G,B四个通道的信息,每个通道用8位来表示,也就是需要4个字节来保存一个像素。

图片文件读取注意事项

1、WebP图片格式问题
虽然Android官方描述是在4.0之后开始支持WebP格式,但在4.0到4.2.1不支持有透明度设置和无损压缩的WebP格式图片。所以如果简单的判断,图片如果是WebP格式,且是4.0以上的系统,就通过BitmapFactory来解析,很可能会出现有些图片能解析,有些又不能解析的情况。此外,还有一些特殊机型,例如NokiaXL虽然是Android4.1的系统,但并不支持WebP编解码。因此,最好是从4.2系统开始用BitmapFactory来解析WebP格式的图片。
2、解析大图时的OOM问题
Android为每个应用分配的堆内存空间是有限的,如果应用请求的内存超过了限制,就会导致OOM。根据手机内存大小的不同,一般应用能够使用的堆内存在几十兆到两三百兆之间。然后应用在读取图片文件时非常消耗内存的,一张手机相机拍出的照片都有3M左右,如果每张图都直接读取,即使不造成OOM,也会导致频繁GC。还有些超大图一张就有几个G,如果直接读取肯定会导致OOM。为了避免读取大图时造成的OOM问题,并减少内存消耗,一般在读取图片时都需要根据图片尺寸来取样。也就是设置BitmapFactory.Options的inSampleSize属性。示例代码如下。

    Options options = new BitmapFactory.Options();    //设置inJustDecodeBounds为true表示只获取大小,不生成Btimap    options.inJustDecodeBounds = true;    //解析图片大小    InputStream stream = getContentResolver().openInputStream(uri);    BitmapFactory.decodeStream(stream, null, options);    stream.close();    int width = options.outWidth;    int height = options.outHeight;    int ratio = 0;    //如果宽度大于高度,交换宽度和高度    if (width > height) {        int temp = width;        width = height;        height = temp;    }    //计算取样比例    it sampleRatio = Math.max(width/900, height/1600);    //定义图片解码选项    Options options = new Options();    options.inSampleSize = sampleRatio;    //读取图片,并将图片缩放到指定的目标大小    InputStream stream = getContentResolver().openInputStream(uri);    Bitmap bitmap = BitmapFactory.decodeStream(stream, null, options);    stream.close();

更多相关文章

  1. 自定义属性时TypedArray的使用方法
  2. Android(安卓)你必须了解的网络框架Retrofit2.0
  3. android kotlin aidl 通讯实现inout类型及回调监听
  4. Android(安卓)APK反编译就这么简单 详解(附图)
  5. 【Android】使用Gradle打包时,获取svn的版本号,删除unalign.apk文
  6. [Android] android的消息队列模型
  7. ym——Android从零开始(1)(Android体系架构及认识)(新)
  8. Android应用程序目录定义及使用方法
  9. android中Manifest文件的语法层次

随机推荐

  1. Android 环境搭建安装ADT访问不到
  2. Android 优化工具
  3. android Tabhost中,控制activity的生命周
  4. Android(安卓)增量更新实例(Smart App Upd
  5. Android(安卓)Edittext 清空按钮功能的实
  6. Camera快门声音去除
  7. Failed to fectch
  8. Android培训班(89)内核解压过程2
  9. Android(安卓)图表绘制 achartengine 示
  10. Android开发环境搭建(jdk+eclip+android s