Android 图像压缩

简介: Android中图片是以Bitmap形式存在的,Bitmap所占内存直接影响应用所占内存大小,Bitmap所占内存大小计算公式:图片长度 * 图片宽度 * 一个像素点占用的字节数Bitmap压缩颜色格式:图1.

Android中图片是以Bitmap形式存在的,Bitmap所占内存直接影响应用所占内存大小,Bitmap所占内存大小计算公式:
图片长度 * 图片宽度 * 一个像素点占用的字节数

Bitmap压缩颜色格式:


img_350954dba4147c9989b51a5581013579.png
图1.png
  1. 质量压缩
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        int quality = 80;
        bitmap.compress(Bitmap.CompressFormat.JPEG, quality, baos);
        byte[] bytes = baos.toByteArray();
        bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

图片的大小没有变化,因为质量有压缩不会减少图片的像素,它是在保持像素的前提下改变图图片的位深及透明度,来达到压缩图片的目的。
注: 当compress方法中第一个参数为Bitmap.CompressFormat.PNG时,quality没有作用,因为png图片时无损的,不能进行压缩。

  1. 采样率压缩
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);

设置inSampleSize的值为2时,宽和高都变为原来的1/2。

注:当设置options.inJustDecodeBounds = true时,BitmapFactory解码图片时会返回空的Bitmap对象,但是也可以返回Bitmap的宽、高以及MimeType。

  1. 缩放法压缩(Martix)
        Matrix matrix = new Matrix();
        matrix.setScale(0.5f, 0.5f);
        Bitmap bitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(),
            bitmap.getHeight(), matrix, true);

其中,bitmap的宽高分别缩小了一半,图片大小压缩成1/4。

  1. RGB_565
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inPreferredConfig = Bitmap.Config.RGB_565;
        Bitmap bitmap = BitmapFactory.decodeFile(path, options);

图片大小直接缩小了一半,宽高没有变。
注意:由于ARGB_4444的画质惨不忍睹,一般假如对图片没有透明度要求的话,可以改成RGB_565,相比ARGB_8888将节省一半的内存开销。

  1. createScaledBitmap
        Bitmap bitmap = BitmapFactory.decodeFile(path);
        bitmap = Bitmap.createScaledBitmap(bitmap, 150, 150, true);

这里将图片压缩成用户所期望的宽高。

系统压缩工具类

从Android 2.2开始系统新增了一个缩略图ThumbnailUtils类,位于framework包下的android.media.ThumbnailUtils位置,可以帮助我们从mediaprovider中获取系统中的视频或图片文件的缩略图,该类提供了三种静态方法可以直接调用获取。
 1、extractThumbnail (source, width, height):
 /**
  * 创建一个指定大小的缩略图
  * @param source 源文件(Bitmap类型)
  * @param width  压缩成的宽度
  * @param height 压缩成的高度
  */
  ThumbnailUtils.extractThumbnail(source, width, height);  

2、extractThumbnail(source, width, height, options):
/**
  * 创建一个指定大小居中的缩略图
  * @param source 源文件(Bitmap类型)
  * @param width  输出缩略图的宽度
  * @param height 输出缩略图的高度
  * @param options 如果options定义为OPTIONS_RECYCLE_INPUT,则回收@param source这个资源文件
  * (除非缩略图等于@param source)
  *
  */
ThumbnailUtils.extractThumbnail(source, width, height, options);  

3、createVideoThumbnail(filePath, kind): 
/**
  * 创建一张视频的缩略图
  * 如果视频已损坏或者格式不支持可能返回null
  * @param filePath 视频文件路径  如:/sdcard/android.3gp
  * @param kind kind可以为MINI_KIND或MICRO_KIND
  *
  */
 ThumbnailUtils.createVideoThumbnail(filePath, kind);

压缩工具类

public class CompressUtils {  
    /** 
     * 按质量压缩 
     * @param bitmap 
     * @return 
     */  
        public static Bitmap compressImage(Bitmap bitmap){  
            ByteArrayOutputStream baos = new ByteArrayOutputStream();  
            //质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中  
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);  
            int options = 100;  
            //循环判断如果压缩后图片是否大于100kb,大于继续压缩  
            while ( baos.toByteArray().length / 1024>100) {  
                //清空baos  
                baos.reset();  
                bitmap.compress(Bitmap.CompressFormat.JPEG, options, baos);  
                options -= 10;//每次都减少10  
            }  
            //把压缩后的数据baos存放到ByteArrayInputStream中  
            ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  
            //把ByteArrayInputStream数据生成图片  
            Bitmap newBitmap = BitmapFactory.decodeStream(isBm, null, null);  
            return newBitmap;  
        }  
  
    /** 
     * 按图片尺寸压缩 参数为路径 
     * @param imgPath 图片路径 
     * @param pixelW 目标图片宽度 
     * @param pixelH 目标图片高度 
     * @return 
     */  
    public static Bitmap compressImageFromPath(String imgPath, int pixelW, int pixelH) {  
        BitmapFactory.Options options = new BitmapFactory.Options();  
        // 开始读入图片,此时把options.inJustDecodeBounds 设回true,即只读边不读内容  
        options.inJustDecodeBounds = true;  
        options.inPreferredConfig = Bitmap.Config.RGB_565;  
        BitmapFactory.decodeFile(imgPath,options);  
        options.inJustDecodeBounds = false;  
        options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );  
        Bitmap bitmap = BitmapFactory.decodeFile(imgPath, options);  
        return bitmap;  
    }  
  
    /** 
     * 按图片尺寸压缩 参数是bitmap 
     * @param bitmap 
     * @param pixelW 
     * @param pixelH 
     * @return 
     */  
    public static Bitmap compressImageFromBitmap(Bitmap bitmap, int pixelW, int pixelH) {  
        ByteArrayOutputStream os = new ByteArrayOutputStream();  
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, os);  
        if( os.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出  
            os.reset();  
            bitmap.compress(Bitmap.CompressFormat.JPEG, 50, os);//这里压缩50%,把压缩后的数据存放到baos中  
        }  
        ByteArrayInputStream is = new ByteArrayInputStream(os.toByteArray());  
        BitmapFactory.Options options = new BitmapFactory.Options();  
        options.inJustDecodeBounds = true;  
        options.inPreferredConfig = Bitmap.Config.RGB_565;  
        BitmapFactory.decodeStream(is, null, options);  
        options.inJustDecodeBounds = false;  
        options.inSampleSize = computeSampleSize(options , pixelH > pixelW ? pixelH : pixelW ,pixelW * pixelH );  
        is = new ByteArrayInputStream(os.toByteArray());  
        Bitmap newBitmap = BitmapFactory.decodeStream(is, null, options);  
        return newBitmap;  
    }  
  
    /** 
     * 对图片进行缩放指定大小 
     * @param bitmap 
     * @param width 
     * @param height 
     * @return 
     */  
    public static Bitmap scaleTo(Bitmap bitmap, int width, int height){  
        int originalWidth = bitmap.getWidth();  
        int originalHeight = bitmap.getHeight();  
        float xScale = (float)width / originalWidth;  
        float yScale = (float)height / originalHeight;  
        Matrix matrix = new Matrix();  
        matrix.setScale(xScale,yScale);  
        return Bitmap.createBitmap(bitmap, 0, 0, originalWidth, originalHeight, matrix, true);  
    }  
  
    /** 
     * 以最省内存的方式读取本地资源的图片 
     * @param context 
     * @param resId 
     * @return 
     */  
    public static Bitmap readBitmap(Context context, int resId) {  
        BitmapFactory.Options opt = new BitmapFactory.Options();  
        opt.inPreferredConfig = Bitmap.Config.RGB_565;  
        opt.inPurgeable = true;  
        opt.inInputShareable = true;  
        // 获取资源图片  
        InputStream is = context.getResources().openRawResource(resId);  
        return BitmapFactory.decodeStream(is, null, opt);  
    }  
  
    /** 
     * android源码提供给我们的动态计算出图片的inSampleSize方法 
     * @param options 
     * @param minSideLength 
     * @param maxNumOfPixels 
     * @return 
     */  
    public static int computeSampleSize(BitmapFactory.Options options, int minSideLength, int maxNumOfPixels) {  
        int initialSize = computeInitialSampleSize(options, minSideLength, maxNumOfPixels);  
        int roundedSize;  
        if (initialSize <= 8) {  
            roundedSize = 1;  
            while (roundedSize < initialSize) {  
                roundedSize <<= 1;  
            }  
        } else {  
            roundedSize = (initialSize + 7) / 8 * 8;  
        }  
        return roundedSize;  
    }  
  
    private static int computeInitialSampleSize(BitmapFactory.Options options,int minSideLength, int maxNumOfPixels) {  
        double w = options.outWidth;  
        double h = options.outHeight;  
        int lowerBound = (maxNumOfPixels == -1) ? 1 : (int) Math.ceil(Math.sqrt(w * h / maxNumOfPixels));  
        int upperBound = (minSideLength == -1) ? 128 :(int) Math.min(Math.floor(w / minSideLength), Math.floor(h / minSideLength));  
        if (upperBound < lowerBound) {  
            return lowerBound;  
        }  
        if ((maxNumOfPixels == -1) && (minSideLength == -1)) {  
            return 1;  
        } else if (minSideLength == -1) {  
            return lowerBound;  
        } else {  
            return upperBound;  
        }  
    }  
}  

有效的处理Bitmap OOM方法

public class BitmapActivity extends Activity {  
    public ImageView imageView = null;  
    @Override  
    protected void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.bitmap_activity);  
        imageView = (ImageView) findViewById(R.id.imageView1);  
        ImageAsyncTask task = new ImageAsyncTask(imageView,50,50);  
        task.execute("http://image.baidu.com/search/down?tn=download&word=download&ie=utf8&fr=detail&url=http%3A%2F%2Fimg1.pconline.com.cn%2Fpiclib%2F200904%2F28%2Fbatch%2F1%2F32910%2F12408824046039mk21hbi75.jpg&thumburl=http%3A%2F%2Fimg2.imgtn.bdimg.com%2Fit%2Fu%3D3414739956%2C4196877666%26fm%3D21%26gp%3D0.jpg");  
    }  
    /** 
     * 计算出压缩比 
     * @param options 
     * @param reqWith 
     * @param reqHeight 
     * @return 
     */  
    public int calculateInSampleSize(BitmapFactory.Options options,int reqWidth,int reqHeight)  
    {  
        //通过参数options来获取真实图片的宽、高  
        int width = options.outWidth;  
        int height = options.outHeight;  
        int inSampleSize = 1;//初始值是没有压缩的  
        if(width > reqWidth || height > reqHeight)  
        {  
            //计算出原始宽与现有宽,原始高与现有高的比率  
            int widthRatio = Math.round((float)width/(float)reqWidth);  
            int heightRatio = Math.round((float)height/(float)reqHeight);  
            //选出两个比率中的较小值,这样的话能够保证图片显示完全   
            inSampleSize = widthRatio < heightRatio ? widthRatio:heightRatio;  
        }  
        System.out.println("压缩比:  "+inSampleSize);  
        return inSampleSize;  
    }  
    /** 
     * 将InputStream转换为Byte数组 
     * @param in 
     * @return 
     */  
    public static byte[] inputStreamToByteArray(InputStream in)  
    {  
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();  
        byte[] buffer = new byte[1024];  
        int len;  
        try {  
            while((len = in.read(buffer)) != -1)  
            {  
                outputStream.write(buffer, 0, len);  
            }  
        } catch (IOException e) {  
            e.printStackTrace();  
        }finally{  
            try {  
                in.close();  
                outputStream.close();  
            } catch (IOException e) {  
                e.printStackTrace();  
            }  
        }  
        return outputStream.toByteArray();  
    }  
      
    class ImageAsyncTask extends AsyncTask<String, Void, Bitmap>  
    {  
        public ImageView iv;  
        public int reqWidth;  
        public int reqHeight;  
        public ImageAsyncTask(ImageView imageView,int reqWidth,int reqHeight)  
        {  
            this.iv = imageView;  
            this.reqWidth = reqWidth;  
            this.reqHeight = reqHeight;  
        }  
        @Override  
        protected Bitmap doInBackground(String... params) {  
            URL url;  
            HttpURLConnection connection = null;  
            InputStream in = null;  
            Bitmap beforeBitmap = null;  
            Bitmap afterBitmap = null;  
            try {  
                url = new URL(params[0]);  
                connection = (HttpURLConnection) url.openConnection();  
                in = connection.getInputStream();  
                BitmapFactory.Options options = new BitmapFactory.Options();  
                //设置BitmapFactory.Options的inJustDecodeBounds属性为true表示禁止为bitmap分配内存  
                options.inJustDecodeBounds = true;  
                byte[] data = inputStreamToByteArray(in);  
                beforeBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);//这次调用的目的是获取到原始图片的宽、高,但是这次操作是没有写内存操作的  
                options.inSampleSize = calculateInSampleSize(options,reqWidth, reqHeight);   
                //设置这次加载图片需要加载到内存中  
                options.inJustDecodeBounds = false;  
                afterBitmap = BitmapFactory.decodeByteArray(data, 0, data.length, options);  
                float afterSize = (float)(afterBitmap.getRowBytes()*afterBitmap.getHeight());  
                System.out.println("压缩之后的图片大小:  "+(float)afterSize/1024+"KB");  
            } catch (Exception e) {  
                System.out.println(e.toString());  
            }  
            return afterBitmap;  
        }  
          
        @Override  
        protected void onPostExecute(Bitmap result) {  
            if(result != null)  
                imageView.setImageBitmap(result);  
        }  
    }  
}

Github图片压

目录
相关文章
|
7月前
|
开发工具 Android开发 开发者
Android UI设计: 解释Android的Nine-Patch图像是什么,它用于什么目的?
Android UI设计: 解释Android的Nine-Patch图像是什么,它用于什么目的?
80 4
|
7月前
|
XML 前端开发 Java
Android App开发图像加工中卡片视图CardView和给图像添加装饰的讲解以及实战(附源码 简单易懂)
Android App开发图像加工中卡片视图CardView和给图像添加装饰的讲解以及实战(附源码 简单易懂)
296 0
|
7月前
|
Android开发
Android Studio入门之图像显示解析及实战(附源码 超详细必看)(包括图像视图、图像按钮、同时展示文本与图像)
Android Studio入门之图像显示解析及实战(附源码 超详细必看)(包括图像视图、图像按钮、同时展示文本与图像)
291 1
|
6月前
|
开发工具 Android开发 开发者
Android `.9.png` 图像是用于UI的可拉伸格式,保持元素清晰度和比例
【6月更文挑战第26天】Android `.9.png` 图像是用于UI的可拉伸格式,保持元素清晰度和比例。通过边上的黑线定义拉伸区域,右下角黑点标识内容区域,适应文本或组件大小变化。常用于按钮、背景等,确保跨屏幕尺寸显示质量。Android SDK 提供`draw9patch.bat`工具来创建和编辑。**
256 6
|
6月前
|
Android开发
在Android上实现图像颜色过滤与反转
在Android上实现图像颜色过滤与反转
194 0
在Android上实现图像颜色过滤与反转
|
7月前
|
Android开发
Android实现圆形图像的两种方法(Glide和Picasso)
Android实现圆形图像的两种方法(Glide和Picasso)
403 1
|
7月前
|
XML 算法 Java
Android Studio App开发之利用图像解码器ImageDecoder播放GIF动图、Webp、HEIF图片(附源码 简单实用)
Android Studio App开发之利用图像解码器ImageDecoder播放GIF动图、Webp、HEIF图片(附源码 简单实用)
426 0
|
7月前
|
XML 安全 Java
Android Studio App开发之绘制简单的动画图像(附源码,简单易懂)
Android Studio App开发之绘制简单的动画图像(附源码,简单易懂)
138 1
|
7月前
|
XML Java Android开发
Android App开发之图像加工中给图像添加水波动态特效(附源码和演示视频 简单易懂)
Android App开发之图像加工中给图像添加水波动态特效(附源码和演示视频 简单易懂)
85 0
|
XML 缓存 API
Android 垃圾分类APP(四)垃圾分类之图像输入
Android 垃圾分类APP(四)垃圾分类之图像输入
297 0
Android 垃圾分类APP(四)垃圾分类之图像输入