图片压缩

来源:互联网 发布:snh48 成员家境 知乎 编辑:程序博客网 时间:2024/04/28 16:56
1. 获取原图片尺寸
通常,我们使用BitmapFactory.decodeResource()方法来从资源文件中读取一张图片并生成一个Bitmap。通过BitmapFactory.Options类类指定解码方法。在解码图片的时候设置inJustDecodeBounds属性设置为true,可以避免内存分配,decodeResource()方法就不会生成Bitmap对象,而仅仅是读取该图片的尺寸和类型信息:
BitmapFactory.Options options = new BitmapFactory.Options();
options.inJustDecodeBounds = true;//true即只读边不读内容
BitmapFactory.decodeResource(getResources(), R.id.myimage, options);
int imageHeight = options.outHeight;
int imageWidth = options.outWidth;
String imageType = options.outMimeType;
为了避免java.lang.OutOfMemeory异常,在解码图片之前就要检查图片的尺寸,除非你十分确信图片资源的尺寸是可预见的并且有着充裕的可用内存。
2. 根据原图尺寸和目标区域的尺寸计算出合适的Bitmap尺寸
BitmapFactory.Options类有一个参数inSampleSize,该参数为int型,他的值指示了在解析图片为Bitmap时在长宽两个方向上像素缩小的倍数。inSampleSize的默认值和最小值为1(当小于1时,解码器将该值当做1来处理),且在大于1时,该值只能为2的幂(当不为2的幂时,解码器会取与该值最接近的2的幂)。例如,当inSampleSize为2时,一个2000*1000的图片,将被缩小为1000*500,相应地,它的像素数和内存占用都被缩小为了原来的1/4:
public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {                                      
    // 原始图片的宽高                                           
    final int height = options.outHeight;
    final int width = options.outWidth;
    int inSampleSize = 1;
    if (height > reqHeight || width > reqWidth) {
        final int halfHeight = height / 2;
        final int halfWidth = width / 2;
        // 在保证解析出的bitmap宽高分别大于目标尺寸宽高的前提下,取可能的inSampleSize的最大值                                
     while ((halfHeight / inSampleSize) > reqHeight                
              && (halfWidth / inSampleSize) > reqWidth) {
            inSampleSize *= 2;                        
        }                                               
}                                              
//高度和宽度都按照此比例进行压缩                                                          
    return inSampleSize;
}                                 


3.根据计算出的inSampleSize生成Bitmap  
public static Bitmap decodeSampledBitmapFromResource(Resources res, int resId,
int reqWidth, int reqHeight) {
// //加载图片的边界信息(只读取图片的高度,宽度,不读取具体字节) 
final BitmapFactory.Options options = new BitmapFactory.Options();
//将此选项设置true,此时再加载图片就会只读取图片边界信息了
options.inJustDecodeBounds = true;
//加载图片边界,并将边界信息封装到options对象  
BitmapFactory.decodeResource(res, resId, options);
// 计算 inSampleSize 的
options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight);
// 根据计算出的 inSampleSize 来解码图片生成Bitmap
 options.inJustDecodeBounds = false;
return BitmapFactory.decodeResource(res, resId, options);
}
这里有一点要注意,就是要在第二遍decode之前把inJustDecodeBounds设置回false。
4. 调用以上的decodeSampledBitmapFromResource方法,使用自定尺寸的Bitmap。
如果你要将一张大图设置为一个100*100的缩略图,执行以下代码:
mImageView.setImageBitmap(decodeSampledBitmapFromResource(getResources(), R.id.myimage, 100, 100));
到此,使用decodeResource()方法将一个大图解析为小尺寸bitmap的应用就完成了。同理,还可以使用decodeStream(),decodeFile()等方法做相同的事,原理是一样的
延伸:一个Bitmap到底占用多大内存?系统给每个应用程序分配多大内存?
· Bitmap占用的内存为:像素总数 * 每个像素占用的内存。在Android中,Bitmap有四种像素类型:ARGB_8888、ARGB_4444、ARGB_565、ALPHA_8,他们每个像素占用的字节数分别为4、2、2、1。因此,一个2000*1000的ARGB_8888类型的Bitmap占用的内存为2000*1000*4=8000000B=8MB。
· Android根据设备屏幕尺寸和dpi的不同,给系统分配的单应用程序内存大小也不同,具体如下表(表格取自Android 4.4 Compatibility Definition Document (CDD)): 
 

 屏幕尺寸 

 DPI 

 应用内存 

 small / normal / large

 ldpi / mdpi

 16MB

 small / normal / large

 tvdpi / hdpi

 32MB

 small / normal / large

 xhdpi

 64MB

 small / normal / large

 400dpi

 96MB

 small / normal / large

 xxhdpi

 128MB

 xlarge

 mdpi

 32MB

 xlarge

 tvdpi / hdpi

 64MB

 xlarge

 xhdpi

 128MB

 xlarge

 400dpi

 192MB

 xlarge

 xxhdpi

 256MB


2)图片在工作线程加载
在工作线程加载并压缩图片:记住:以后在activity写内部类时,内部类又是一个线程对象,请尽量使用静态内部类
继承AsyncTask,利用弱引用,引用外部类的中的属性imageView
在doInBackground 返回值为使用decodeResource()方法,将一个大图解析为小尺寸bitmap的应用  
onPostExecute用if判断弱引用与(bitmap)result是否为空如果为空返回,不为空则提取imageview,再用if语句imageview判断是否为空,如果不为空则将bitmap写进imageview
1 0
原创粉丝点击