软件世界网 购物 网址 三丰软件 | 小说 美女秀 图库大全 游戏 笑话 | 下载 开发知识库 新闻 开发 图片素材
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
移动开发 架构设计 编程语言 Web前端 互联网
开发杂谈 系统运维 研发管理 数据库 云计算 Android开发资料
  软件世界网 -> 移动开发 -> 图片压缩CompressUtil解析 -> 正文阅读

[移动开发]图片压缩CompressUtil解析


  • CompressUtil 流程图:
    [img]http://img.blog.csdn.net/20160401182511845


CompressUtil 类 详解
public class CompressUtil {
/**
 * 最终封装的压缩方法
 * @param imgPath
 * @return
 */
public static Bitmap process(String imgPath){
    int degree = readPictureDegree(imgPath);    //获取旋转角度
    Bitmap bmp =getBmpByMaxSize(imgPath, 480);  //获取当前路径图片的位图,最大尺寸180*1024
    if (degree != 0) {
        bmp = rotaingImageView(degree, bmp);    //有旋转角度的旋转角度
    }
    return bmp;
}
/**
 * 读取图片属性:旋转的角度
 * @param path 图片绝对路径
 * @return degree旋转的角度
 */
@SuppressLint("NewApi")
public static int readPictureDegree(String path){
    int degree  = 0;    //设置默认图片角度为0度
    try {
        /**
         * Exif: 图像信息
         * Exif是一种图像文件格式,它的数据存储与JPEG格式是完全相同的。
         * 实际上Exif格式就是在JPEG格式头部插入了数码照片的信息,包括
         * 拍摄时的光圈、快门、白平衡、ISO、焦距、日期时间等各种和拍摄
         * 条件以及相机品牌、型号、色彩编码、拍摄时录制的声音以及GPS全
         * 球定位系统数据、缩略图等。你可以利用任何可以查看JPEG文件的
         * 看图软件浏览Exif格式的照片,但并不是所有的图形程序都能处理
         * Exif信息。
         *
         * ExifInterface:  图像信息接口类
         */

        /**
         * ExifInterface构造函数   ExifInterface(String filename)
         * 从指定的JPEG文件中读取EXIF标签。
         */

        //获取指定图片的图片处理对象
        ExifInterface exifInterface = new ExifInterface(path);

        /**
         * int <- getAttributeInt(String tag, int defaultValue)
         * 返回指定标记的整数值
         *
         * Attribute : 属性
         *
         * tag : 指标 (一共21个)
         * ExifInterface.TAG_APERTURE => 光圈指标
         * ExifInterface.TAG_DATETIME => 日期时间指标
         * ExifInterface.TAG_EXPOSURE_TIME => 发布时间指标
         * ExifInterface.TAG_FLASH => 闪光灯
         * ExifInterface.TAG_FOCAL_LENGTH => 焦距指标
         * ExifInterface.TAG_GPS_ALTITUDE => GPS海拔高度指标
         * ExifInterface.TAG_GPS_ALTITUDE_REF => GPS海拔参考点指标
         * ExifInterface.TAG_GPS_DATESTAMP => GPS日期戳指标
         * ExifInterface.TAG_GPS_LATITUDE => GPS纬度指标
         * ExifInterface.TAG_GPS_LATITUDE_REF => GPS纬度参考点指标
         * ExifInterface.TAG_GPS_LONGITUDE => GPS经度指标
         * ExifInterface.TAG_GPS_LONGITUDE_REF => GPS经度参考点指标
         * ExifInterface.TAG_GPS_PROCESSING_METHOD => GPS加工指标
         * ExifInterface.TAG_GPS_TIMESTAMP => GPS时间戳指标
         * ExifInterface.TAG_IMAGE_LENGTH => 图像长度指标
         * ExifInterface.TAG_IMAGE_WIDTH => 图像宽度指标
         * ExifInterface.TAG_ISO => 标签
         * ExifInterface.TAG_MAKE => 制作
         * ExifInterface.TAG_MODEL => 模型
         * ExifInterface.TAG_ORIENTATION => 定位指标
         * ExifInterface.TAG_WHITE_BALANCE => 白平衡指标
         *
         * defaultValue : 默认值 (一共11个)
         *
         * ExifInterface.ORIENTATION_FLIP_HORIZONTAL => 水平翻转
         * ExifInterface.ORIENTATION_FLIP_VERTICAL => 垂直翻转
         * ExifInterface.ORIENTATION_NORMAL => 正常
         * ExifInterface.ORIENTATION_ROTATE_180 => 旋转180度
         * ExifInterface.ORIENTATION_ROTATE_270 => 旋转270度
         * ExifInterface.ORIENTATION_ROTATE_90 => 旋转90度
         * ExifInterface.ORIENTATION_TRANSPOSE => 取向的转置
         * ExifInterface.ORIENTATION_TRANSVERSE => 方位横向
         * ExifInterface.ORIENTATION_UNDEFINED => 方向未定义
         * ExifInterface.WHITEBALANCE_AUTO => 白平衡自动
         * ExifInterface.WHITEBALANCE_MANUAL => 手动白平衡
         */

        // 获取该图片的方向参数
        int orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_ROTATE_90);
        switch (orientation) {
            case ExifInterface.ORIENTATION_ROTATE_90:
                degree = 90;
                break;
            case ExifInterface.ORIENTATION_ROTATE_180:
                degree = 180;
                break;
            case ExifInterface.ORIENTATION_ROTATE_270:
                degree = 270;
                break;
        }
    } catch (IOException e) {
        e.printStackTrace();
    }
    return degree;
}

/**
 * 旋转图片
 * @param angle
 * @param bitmap
 * @return Bitmap
 *
 * Bitmap : 位图
 * Bitmap是Android系统中的图像处理的最重要类之一。
 * 用它可以获取图像文件信息,进行图像剪切、旋转、缩
 * 放等操作,并可以指定格式保存图像文件。
 *
 * Bitmap实现在android.graphics包中。但是Bitmap
 * 类的构造函数是私有的,外面并不能实例化,只能是通
 * 过JNI实例化。这必然是 某个辅助类提供了创建Bitmap
 * 的接口,而这个类的实现通过JNI接口来实例化Bitmap的,
 * 这个类就是BitmapFactory。
 *
 * decode : 解码
 *
 * BitmapFactory.decodeFile(String pathName)
 * BitmapFactory.decodeFile(String pathName,Options opts)
 * BitmapFactory.decodeResource(Resource res,int id)
 * BitmapFactory.decodeResource(Resource res,int id,Options opts)
 *
 * 利用BitmapFactory可以从一个指定文件中,利用decodeFile()解出Bitmap;
 * 也可以定义的图片资源中,利用decodeResource()解出Bitmap
 *
 * 其中Options是decode时的选项
 * 在使用方法decodeFile()/decodeResource()时,都可以指定一个BitmapFacotry.Options。
 *
 * 利用Options的下列属性,可以指定decode的选项
 * inPreferredConfig => decode到内存中,手机中所采用的编码,可选值定义在Bitmap.Config中。缺省值是ARGB_8888
 * inJustDecodeBounds => 如果设置为true,并不会把图像的数据完全解码,亦即decodeXyz()返回值为null,但是Options的outAbc中解出了图像的基本信息
 * inSampleSize => 设置decode时的缩放比例
 *
 */
public static Bitmap rotaingImageView(int angle , Bitmap bitmap) {

    // Bitmap可以和Matrix结合实现图像的剪切、旋转、缩放等操作

    //获取Matrix对象
    Matrix matrix = new Matrix();
    //设置旋转角度
    matrix.postRotate(angle);
    // 创建新的图片
    Bitmap resizedBitmap=bitmap;

    /**
     * 用原Bitmap通过变换生成新的Bitmap的方法:
     *
     * public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height,Matrix m,boolean filter)
     * 这种方法是最终的实现,后两种只是对这种方法的封装
     *
     * public static Bitmap createBitmap(Bitmap source,int x,int y,int width,int height)
     * 这种方法可以从源Bitmap中指定区域(x,y, width, height)中挖出一块来实现剪切
     *
     * public static Bitmap createScaledBitmap(Bitmap src,int dstWidth,int dstHeight,boolean filter)
     * 这种方法可以把源Bitmap缩放为dstWidth x dstHeight的Bitmap
     *
     * filter => 设为true => 对Bitmap进行滤波处理,会有抗锯齿的效果
     */

    try {
        resizedBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, true);

        /**
         * Bitmap的recycle问题
         * recycle : 回收
         * 虽然Android有自己的垃圾回收机制,对于是不是要我们自己调用recycle,还的看情况而定。
         * 如果只是使用少量的几张图片,回收与否关系不大。可是若有大量bitmap需要垃圾回收处理,
         * 那必然垃圾回收需要做的次数就更多也发生地更频繁,会对系统资源造成负荷。所以,这个时
         * 候还是自己试用recycle来释放的比较好。
         *
         * 注意 => 只有当你确认你不会在使用这个bitmap的时候,就可以选择调用recycle()方法释放它。
         *
         */
        bitmap.recycle();

        //进行垃圾回收
        System.gc();
    }catch (OutOfMemoryError e){
        e.printStackTrace();
    }
    return resizedBitmap;
}

/**
 * 减少图片质量压缩
 * @param bmp
 * @param maxSize
 * @param fileSize
 * @return
 */
public static Bitmap compressBmp(Bitmap bmp, int maxSize, long fileSize) {
    Bitmap newBmp=bmp;
    //ByteArrayOutputStream => 捕获内存缓冲区的数据,转换成字节数组。
    ByteArrayOutputStream baos=null;
    //ByteArrayInputStream => 将字节数组转化为输入流
    ByteArrayInputStream bais=null;

    int quality = 100;
    if(fileSize>4*1024*1024){
        quality=40;
    }else if(fileSize>2*1024*1024){
        quality=50;
    }else if(fileSize>800*1024){
        quality=60;
    }
    try {
        /**
         * ByteArrayOutputStream类是在创建它的实例时,程序内部创建一个byte型别数组的缓冲区,
         * 然后利用ByteArrayOutputStream和ByteArrayInputStream的实例向数组中写入或读出
         * byte型数据。在网络传输中我们往往要传输很多变量,我们可以利用ByteArrayOutputStream
         * 把所有的变量收集到一起,然后一次性把数据发送出去。
         */
        baos = new ByteArrayOutputStream();
        System.out.print("开始压缩: " + quality);
        /**
         * 图片压缩
         * Bitmap.compress(CompressFormat format, int quality, OutputStream stream)
         * 方法的参数format可设置JPEG或PNG格式;quality可选择压缩质量;fOut是输出流(OutputStream)
         */
        bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
        float maxByte = maxSize * 1024;
        baos.flush();
        float scale = 1;
        while (baos.size() > maxByte) {
            System.out.print("压缩大小:" + baos.size() / 1024);
            System.out.print("压缩大小2:" + baos.toByteArray().length / 1024);
            scale = Math.round((float) baos.size() / maxByte);
            if (scale < 1) scale = 1;
            quality -= scale * 2;
            baos.reset();   //重置流,使流计数=0。重置该流丢弃所有当前累积输出。
            bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
            baos.flush();
        }

// File file=new File(FileUtil.getAudioPath()+File.separator+System.currentTimeMillis()+”.jpg”);
// BufferedOutputStream bos=new BufferedOutputStream(new FileOutputStream(file));
// bos.write(baos.toByteArray());
// bos.flush();
// bos.close();
        System.out.print("压缩后大小:" + baos.size() / 1024);
        bais = new ByteArrayInputStream(baos.toByteArray());
        baos.flush();
        newBmp = BitmapFactory.decodeStream(bais);
        bmp.recycle();
        System.gc();
    }catch (OutOfMemoryError e){
        e.printStackTrace();
        //内存溢出则压缩更多
        if(!bmp.isRecycled()) {
            try {
                quality=quality/2;
                baos = new ByteArrayOutputStream();
                bmp.compress(Bitmap.CompressFormat.JPEG, quality, baos);
                bais = new ByteArrayInputStream(baos.toByteArray());
                baos.flush();
                newBmp = BitmapFactory.decodeStream(bais);
            }catch (Exception ex){}
        }
    }catch (Exception e){
        e.printStackTrace();
    }finally{
        try {
            if (bais != null) {
                bais.close();
            }
            if (baos != null) {
                baos.close();
            }
        }catch (Exception e){}
    }
    return newBmp;
}


public static Bitmap getBmpByMaxSize(String path, int maxSize){
    /**
     * Android使用BitmapFactory.Options解决加载大图片内存溢出问题
     *
     * 由于Android对图片使用内存有限制,若是加载几兆的大图片便内存溢出。
     * Bitmap会将图片的所有像素(即长x宽)加载到内存中,如果图片分辨率
     * 过大,会直接导致内存溢出(java.lang.OutOfMemoryError),只有
     * 在BitmapFactory加载图片时使用BitmapFactory.Options对相关参
     * 数进行配置来减少加载的像素。
     *
     * BitmapFactory.Options这个类,有一个字段叫做 inJustDecodeBounds 。
     * 如果我们把它设为true,那么BitmapFactory.decodeFile(String path,
     * Options opt)并不会真的返回一个Bitmap给你,它仅仅会把它的宽,高取回
     * 来给你,这样就不会占用太多的内存,也就不会那么频繁的发生OOM(Out Of
     * Memory)了。
     *
     */
    BitmapFactory.Options options=new BitmapFactory.Options();
    //使图片最小边框缩小到800像素
    options.inJustDecodeBounds=true;
    //这里返回的bitmap=null,但可以通过options.outWidth 和 options.outHeight就是我们想要的宽和高了
    BitmapFactory.decodeFile(path, options);
    //最短的永远都是宽度
    double width=options.outWidth<options.outHeight? options.outWidth: options.outHeight;
    //实际宽度/理想宽度 => 上传图片缩放比例
    int sampleSize=(int)Math.round(width/480);
    System.out.print("上传图片缩放比例:" + sampleSize);
    if(sampleSize<1) sampleSize=1;
    /**
     * inSampleSize表示缩略图大小为原始图片大小的几分之一,
     * 即如果这个值为2,则取出的缩略图的宽和高都是原始图片的1/2,图片大小就为原始大小的1/4。
     */
    options.inSampleSize = sampleSize;
    options.inJustDecodeBounds=false;
    options.inDither=false;    /*不进行图片抖动处理*/
    options.inPreferredConfig=null;  /*设置让解码器以最佳方式解码*/
    /**
     * 下面两个字段需要组合使用  节约内存
     */
    options.inPurgeable = true; // 允许可清除
    options.inInputShareable = true;

    long length=new File(path).length();
    if(length>(1.5*1024*1024)){  //大于1.5M时
        options.inSampleSize+=(int)(length/1024/1024)*0.5; //当大于2M时为避免内存溢出缩小
    }

    Bitmap bitmap=null;
    try {
        bitmap = BitmapFactory.decodeFile(path, options);
    }catch (OutOfMemoryError e){
        e.printStackTrace();
        //内存溢出则将图片缩小一半
        if(options.inSampleSize<1) options.inSampleSize=1;
        options.inSampleSize=options.inSampleSize*2;
        bitmap=BitmapFactory.decodeFile(path, options);
    }
    if(length>(800*1024)) { //大于20K字节压缩
        bitmap = compressBmp(bitmap, maxSize, length);
    }

    return bitmap;
}

}
......显示全文...
    点击查看全文


上一篇文章      下一篇文章      查看所有文章
2016-04-02 20:51:48  
移动开发 最新文章
深入了解android中的消息机制Handler
Android
Libgdx之BitmapFont字体
AndroidApp发布到应用市场的流程
Android开发找工作之前先看看这些知识点吧
View的事件分发机制解析
简单介绍了解白鹭引擎Egret
Cocos2d
android获取本地图片(二)
动画特效七:碰撞动画
360图书馆 软件开发资料 文字转语音 购物精选 软件下载 美食菜谱 新闻资讯 电影视频 小游戏 Chinese Culture 股票 租车
生肖星座 三丰软件 视频 开发 短信 中国文化 网文精选 搜图网 美图 阅读网 多播 租车 短信 看图 日历 万年历 2018年6日历
2018-6-23 14:21:02
多播视频美女直播
↓电视,电影,美女直播,迅雷资源↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  软件世界网 --