Android Bitmap使用

Posted 花姓-老花

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android Bitmap使用相关的知识,希望对你有一定的参考价值。

android开发中,跟图片打交道在正常不过了。不过如果图片处理不当就会造成内存溢出(OOM),所以了解Bitmap相关用法就有必要了,Bitmap在Android中指的是一张图片,图片类型可以是png、jpg等。

1、BitmapFactory

BitmapFactory进一步封装了获取Bitmap对象,BitmapFactory提供获取Bitmap对象的方法有以下:

    1、decodeByteArray     从指定字节数组中获取

    用法:

private void getBitmap()
        //主要是把网络图片的数据流读入到内存中
        new Thread(new Runnable() 
            @Override
            public void run() 
                //Android4.0以后,网络请求一定要在子线程中进行
                final byte[] data = getImages("http://img5.imgtn.bdimg.com/it/u=274881988,3237971911&fm=27&gp=0.jpg");
                //更新UI可以在runOnUiThread这个方法或通过handler
                runOnUiThread(new Runnable() 
                    @Override
                    public void run() 
                        Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);
                        mImage.setImageBitmap(bitmap);
                    
                );

            
        ).start();
    

private byte[] getImages(String path)
        try 
            URL url = new URL(path);
            HttpURLConnection httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.setReadTimeout(6*1000);
            InputStream inputStream = null;
            ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
            byte[] buffer = new byte[1024];
            int len = -1;
            if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK)
                inputStream = httpURLConnection.getInputStream();
                while ((len = inputStream.read(buffer)) !=-1)
                    outputStream.write(buffer,0,len);
                
                outputStream.close();
                inputStream.close();
            
            return outputStream.toByteArray();
         catch (MalformedURLException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        
        return null;
    

注:网络请求一定要在子线程中来做,否则就会抛NetworkOnMainThreadException异常


    2、decodeFile、decodeFileDescriptor   从指定文件中获取

    用法:

private void getDecodeFileBitmap() 
        String fileName = "/sdcard/daniulivelogo.png";  //自己手机sd图片路径
        File file = new File(fileName);
        if (file.exists()) 
            Bitmap bm = BitmapFactory.decodeFile(fileName);
            mImage.setImageBitmap(bm);
         else 
            Log.v("hjz","文件不存在");
        
    

    注:sd相对路径

        有外置内存:/sdcard/Android/data/<application package>/cache

        无外置内存:/data/data/Android/data/<application package>/cache

    3、decodeResource   从指定本地资源获取

private void getDecodeResourceBitmap()
        Bitmap bitmap = BitmapFactory.decodeResource(getResources(),R.mipmap.ic_launcher);
        mImage.setImageBitmap(bitmap);
    

    4、decodeStream   从输入流中获取

    用法:

private void getDecodeStreamBitmap()
        new Thread(new Runnable() 
            @Override
            public void run() 
                final Bitmap bitmap = getImageFromNet("http://img5.imgtn.bdimg.com/it/u=274881988,3237971911&fm=27&gp=0.jpg");
                runOnUiThread(new Runnable() 
                    @Override
                    public void run() 
                        mImage.setImageBitmap(bitmap);
                    
                );
            
        ).start();
    

private Bitmap getImageFromNet(String path) 
        HttpURLConnection httpURLConnection = null;
        InputStream inputStream = null;
        try 
            URL url = new URL(path);
            httpURLConnection = (HttpURLConnection) url.openConnection();
            httpURLConnection.setRequestMethod("GET");
            httpURLConnection.setReadTimeout(6 * 1000);
            if (httpURLConnection.getResponseCode() == HttpURLConnection.HTTP_OK) 
                inputStream = httpURLConnection.getInputStream();
                return BitmapFactory.decodeStream(inputStream);
            
         catch (MalformedURLException e) 
            e.printStackTrace();
         catch (IOException e) 
            e.printStackTrace();
        finally 
            if (inputStream != null)
                try 
                    inputStream.close();
                    inputStream = null;
                 catch (IOException e) 
                    e.printStackTrace();
                
            
            if (httpURLConnection != null)
                httpURLConnection.disconnect();
            
        
        return null;
    

上面是没有添加任何策略的常规用法,容易造成OOM现象。


2、BitmapFactory.Options

    高效加载类图其核心就是BitmapFactory.Options,在开发中很多时候并不需要图片原图大小和原图这么高清,目的对图片进行采样压缩,降低内存占有空间,从而减少了内存溢出(OOM)问题。

    1、BitmapFactory.Options常用方法:

参数说明备注
inJustDecodeBounds

如果设置为true,则只返回bitmap尺寸,没有为

其分配内存

如果设置为false,解码返回位图,为其分配内存

预加载图片,获取图片尺寸
inSampleSize

当设置值>1时,按相关比例缩放bitmap宽高,返回较小图片在保存

当设置值<=1时,就当做1来处理

案例:width=100,height=100,inSampleSize = 2,

则返回width=50,height=50,像素50*50=250  

图片将为了原来1/4

inPreferredConfig

如果这是非空的,解码器将尝试解码到这个内部配置。如果它是null,

或者请求不能满足,解码器将尝试根据系统的屏幕深度选择

最佳匹配配置,以及原始图像的特性,例如它是否具有每像素alpha

(需要配置)。图像加载默认的配置argb_8888。

另外:色彩模式,默认argb_8888,一个像素4byte。如果对透明不做要求,采用rgb_565,一个像素2byte

alpha_8:每个像素1byte

argb_444:每个像素2byte

argb_8888:每个像素4byte

rgb_565:每个像素2byte

场景:如果一张图片分辨率1024*768,

采用argb_8888,占用空间为1024*768*4=3M;

如果采用argb_444内存就能减半

inPremultiplied默认true,返回的bitmap颜色通道上预先附加透明通道

透明通道是计算机图形学术语,指的是“非彩色”通道,

8位灰度通道,使用256级灰度来记录图像中的透明信息,

定义透明、不透明和半透明。如32位存储的图片,

8红+8绿+8蓝+8透明;

inDither抖动解码,默认false,标识不采用抖动解码

Bitmap解码是根据它所记录的节点,按照一定的算法来

补充两个节点之间的数据,可理解为补充像素点的颜色。

一张颜色丰富的图用一个位数比较低的颜色模式解码的话,

会感觉颜色不够用,颜色渐变区域有明显断裂带。

因为一些丰富的颜色在位数较低的颜色模式下并没有,

只能用相近的颜色补充,可能一大片没有,那么这大片

都用一个颜色填充,就形成了断裂色带;如果采用

抖动解码,就会在这些颜色上采用随机噪声色来填充,这样

显示效果更好,色带不那么明显。如果不想有这些色带,就需要采用抖动解码

inScaled设置这个bitmap是否可以被缩放,默认值为true 
inDensity表示这个bitmap的像素密度 
inTargetDensity目标位图的像素密度将被绘制到 
inScreenDensity正在使用的实际屏幕的像素密度

inDensity,inTargetDensity,inScreenDensity

这三个值的目的就是为了确定这个Bitmap的宽高和density。详细算法可以查看

setDensityFromOptions()方法源码实现;

inPurgeable/inInputShareable

一般一起使用,设置为true时,表示空间不够可以被释放,

后者表示是否可以共享引用。Android5.0后被弃用

 
outWidth/outHeight表示bitmap的宽和高,一般和inJustDecodeBounds一起使用获取Bitmap的宽高,但不加载到内存中 

    2、BitmapFactory.Options实战

String fileName = "/sdcard/aa.jpg";
BitmapFactory.Options options = getBitmapFactory(fileName,400,400);
mImage2.setImageBitmap(BitmapFactory.decodeFile(fileName,options));

private BitmapFactory.Options getBitmapFactory(String fileName,int pixelW,int pixelH)
        BitmapFactory.Options options = new BitmapFactory.Options();
        File file = new File(fileName);
        if (file.exists())
            //inJustDecodeBounds为true,不返回bitmap,只返回这个bitmap的尺寸
            options.inJustDecodeBounds = true;
            //设置图片色彩
            options.inPreferredConfig = Bitmap.Config.RGB_565;
            //预加载图片
            Bitmap bitmap = BitmapFactory.decodeFile(fileName,options);
            //获取原始图片宽高
            int originalW = options.outWidth;
            int originalH = options.outHeight;
            //上面设置为true获取bitmap尺寸大小,在这里一定要重新设置为false,否则位图加载不出来
            options.inJustDecodeBounds = false;
            options.inSampleSize = getSampleSize(originalW,originalH,pixelW,pixelH);
            return options;
        
        return null;
    

    /**
     *
     * @param originalW 原图宽
     * @param originalH 原图高
     * @param pixelW 指定图片宽度
     * @param pixelH 指定图片高度
     * @return 返回原图缩放大小
     */
    private int getSampleSize(int originalW, int originalH, int pixelW, int pixelH) 
        int simpleSize = 1;
        if (originalW > originalH && originalW > pixelW)
            simpleSize = originalW / pixelW;
        else if (originalH > originalW && originalH > pixelH)
            simpleSize = originalH / pixelH;
        
        if (simpleSize <=0)
            simpleSize = 1;
        
        return simpleSize;
    

以上是关于Android Bitmap使用的主要内容,如果未能解决你的问题,请参考以下文章

Android Bitmap着色

色带仅适用于 Android 4.0+

Android中获取图片的宽和高

Bitmap处理--BitmapShader、目标大小Bitmap、遮罩图层

delphi GDI 图片压缩代码 据说是位图缩放保持原图视觉效果最好的算法

Android摄像头开发:拍照后添加相框,融合相框和图片为一副 图片