传递Bitmap + 图片压缩处理 并保存 + 壁纸设置 总结

原博客地址:http://blog.csdn.net/moubenmao_jun/article/details/20055189

===================================================================================================================================

基本思路是先把bitmap转化为byte数组,用Intent传递数组,在将数组转化为bitmap

bitmap转化为byte数组的方法:

[java]  view plain copy print ?
  1. private byte[] Bitmap2Bytes(Bitmap bm){       
  2.     ByteArrayOutputStream baos = new ByteArrayOutputStream();         
  3.     bm.compress(Bitmap.CompressFormat.PNG, 100, baos);         
  4.     return baos.toByteArray();       
  5.    }   
byte数组转化为bitmap方法:
[java]  view plain copy print ?
  1. byte buff[]=mIntent.getByteArrayExtra("image");    
  2. bitmap = BitmapFactory.decodeByteArray(buff, 0, buff.length);   
=================================================================================================================

首先该文章是总结, 不是原创, 是通过看网上其他大神的文章和自己的一些实践总结出来的. 

一.图片的存在形式

1.文件形式(即以二进制形式存在于硬盘上)
2.流的形式(即以二进制形式存在于内存中)
3.Bitmap形式
这三种形式的区别: 文件形式和流的形式对图片体积大小并没有影响,也就是说,如果你手机SD卡上的如果是100K,那么通过流的形式读到内存中,也一定是占100K的内存,注意是流的形式,不是Bitmap的形式,当图片以Bitmap的形式存在时,其占用的内存会瞬间变大, 我试过500K文件形式的图片加载到内存,以Bitmap形式存在时,占用内存将近10M,当然这个增大的倍数并不是固定的

检测图片三种形式大小的方法:
文件形式: file.length()
流的形式: 讲图片文件读到内存输入流中,看它的byte数
Bitmap:    bitmap.getByteCount()

二.常见的压缩方式

1. 将图片保存到本地时进行压缩, 即将图片从Bitmap形式变为File形式时进行压缩,
    特点是:  File形式的图片确实被压缩了, 但是当你重新读取压缩后的file为 Bitmap是,它占用的内存并没有改变  

[java]  view plain copy print ?
  1. public static void compressBmpToFile(Bitmap bmp,File file){  
  2.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  3.         int options = 80;//个人喜欢从80开始,  
  4.         bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);  
  5.         while (baos.toByteArray().length / 1024 > 100) {   
  6.             baos.reset();  
  7.             options -= 10;  
  8.             bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);  
  9.         }  
  10.         try {  
  11.             FileOutputStream fos = new FileOutputStream(file);  
  12.             fos.write(baos.toByteArray());  
  13.             fos.flush();  
  14.             fos.close();  
  15.         } catch (Exception e) {  
  16.             e.printStackTrace();  
  17.         }  
  18.     }  

方法说明: 该方法是压缩图片的质量, 注意它不会减少图片的像素,比方说, 你的图片是300K的, 1280*700像素的, 经过该方法压缩后, File形式的图片是在100以下, 以方便上传服务器, 但是你BitmapFactory.decodeFile到内存中,变成Bitmap时,它的像素仍然是1280*700, 计算图片像素的方法是 bitmap.getWidth()和bitmap.getHeight(), 图片是由像素组成的, 每个像素又包含什么呢? 熟悉PS的人知道, 图片是有色相,明度和饱和度构成的. 
该方法的官方文档也解释说, 它会让图片重新构造, 但是有可能图像的位深(即色深)和每个像素的透明度会变化,JPEG onlysupports opaque(不透明), 也就是说以jpeg格式压缩后, 原来图片中透明的元素将消失.所以这种格式很可能造成失真
既然它是改变了图片的显示质量, 达到了对File形式的图片进行压缩, 图片的像素没有改变的话, 那重新读取经过压缩 的file为Bitmap时, 它占用的内存并不会少.(不相信的可以试试)

因为: bitmap.getByteCount() 是计算它的像素所占用的内存, 请看官方解释: Returns the number of bytes used to  store this bitmap's pixels.
2.    将图片从本地读到内存时,进行压缩 ,即图片从File形式变为Bitmap形式
       特点: 通过设置采样率, 减少图片的像素, 达到对内存中的Bitmap进行压缩
       先看一个方法: 该方法是对内存中的Bitmap进行质量上的压缩, 由上面的理论可以得出该方法是无效的, 而且也是没有必要的, 因为你已经将它读到内存中了,再压缩多此一举, 尽管在获取系统相册图片时,某些手机会直接返回一个Bitmap, 但是这种情况下, 返回的Bitmap都是经过压缩的, 它不可能直接返回一个原声的Bitmap形式的图片, 后果可想而知

[java]  view plain copy print ?
  1. private Bitmap compressBmpFromBmp(Bitmap image) {  
  2.         ByteArrayOutputStream baos = new ByteArrayOutputStream();  
  3.         int options = 100;  
  4.         image.compress(Bitmap.CompressFormat.JPEG, 100, baos);  
  5.         while (baos.toByteArray().length / 1024 > 100) {   
  6.             baos.reset();  
  7.             options -= 10;  
  8.             image.compress(Bitmap.CompressFormat.JPEG, options, baos);  
  9.         }  
  10.         ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());  
  11.         Bitmap bitmap = BitmapFactory.decodeStream(isBm, nullnull);  
  12.         return bitmap;  
  13.     }  
  再看一个方法:
[java]  view plain copy print ?
  1.     private Bitmap compressImageFromFile(String srcPath) {  
  2.         BitmapFactory.Options newOpts = new BitmapFactory.Options();  
  3.         newOpts.inJustDecodeBounds = true;//只读边,不读内容  
  4.         Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);  
  5.   
  6.         newOpts.inJustDecodeBounds = false;  
  7.         int w = newOpts.outWidth;  
  8.         int h = newOpts.outHeight;  
  9.         float hh = 800f;//  
  10.         float ww = 480f;//  
  11.         int be = 1;  
  12.         if (w > h && w > ww) {  
  13.             be = (int) (newOpts.outWidth / ww);  
  14.         } else if (w < h && h > hh) {  
  15.             be = (int) (newOpts.outHeight / hh);  
  16.         }  
  17.         if (be <= 0)  
  18.             be = 1;  
  19.         newOpts.inSampleSize = be;//设置采样率  
  20.           
  21.         newOpts.inPreferredConfig = Config.ARGB_8888;//该模式是默认的,可不设  
  22.         newOpts.inPurgeable = true;// 同时设置才会有效  
  23.         newOpts.inInputShareable = true;//。当系统内存不够时候图片自动被回收  
  24.           
  25.         bitmap = BitmapFactory.decodeFile(srcPath, newOpts);  
  26. //      return compressBmpFromBmp(bitmap);//原来的方法调用了这个方法企图进行二次压缩  
  27.                                     //其实是无效的,大家尽管尝试  
  28.         return bitmap;  
  29.     }  

下面是网上另外三种图片压缩的方法 :

第一:我们先看下质量压缩方法:

[java]  view plain copy print ?
  1. private Bitmap compressImage(Bitmap image) {    
  2.     
  3.         ByteArrayOutputStream baos = new ByteArrayOutputStream();    
  4.         image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中    
  5.         int options = 90;    
  6.         while ( baos.toByteArray().length / 1024>100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩           
  7.             baos.reset();//重置baos即清空baos    
  8.             image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中    
  9.             options -= 10;//每次都减少10    
  10.         }    
  11.         ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中    
  12.         Bitmap bitmap = BitmapFactory.decodeStream(isBm, nullnull);//把ByteArrayInputStream数据生成图片    
  13.         return bitmap;    
  14.     }   

第二:图片按比例大小压缩方法(根据路径获取图片并压缩):

[java]  view plain copy print ?
  1. private Bitmap getimage(String srcPath) {    
  2.         BitmapFactory.Options newOpts = new BitmapFactory.Options();    
  3.         //开始读入图片,此时把options.inJustDecodeBounds 设回true了    
  4.         newOpts.inJustDecodeBounds = true;    
  5.         Bitmap bitmap = BitmapFactory.decodeFile(srcPath,newOpts);//此时返回bm为空    
  6.             
  7.         newOpts.inJustDecodeBounds = false;    
  8.         int w = newOpts.outWidth;    
  9.         int h = newOpts.outHeight;    
  10.         //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为    
  11.         float hh = 800f;//这里设置高度为800f    
  12.         float ww = 480f;//这里设置宽度为480f    
  13.         //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可    
  14.         int be = 1;//be=1表示不缩放    
  15.         if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放    
  16.             be = (int) (newOpts.outWidth / ww);    
  17.         } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放    
  18.             be = (int) (newOpts.outHeight / hh);    
  19.         }    
  20.         if (be <= 0)    
  21.             be = 1;    
  22.         newOpts.inSampleSize = be;//设置缩放比例    
  23.         //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了    
  24.         bitmap = BitmapFactory.decodeFile(srcPath, newOpts);    
  25.         return compressImage(bitmap);//压缩好比例大小后再进行质量压缩    
  26.     }    
第三:图片按比例大小压缩方法(根据Bitmap图片压缩):
[java]  view plain copy print ?
  1. private Bitmap comp(Bitmap image) {    
  2.         
  3.     ByteArrayOutputStream baos = new ByteArrayOutputStream();           
  4.     image.compress(Bitmap.CompressFormat.JPEG, 100, baos);    
  5.     if( baos.toByteArray().length / 1024>1024) {//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出      
  6.         baos.reset();//重置baos即清空baos    
  7.         image.compress(Bitmap.CompressFormat.JPEG, 50, baos);//这里压缩50%,把压缩后的数据存放到baos中    
  8.     }    
  9.     ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());    
  10.     BitmapFactory.Options newOpts = new BitmapFactory.Options();    
  11.     //开始读入图片,此时把options.inJustDecodeBounds 设回true了    
  12.     newOpts.inJustDecodeBounds = true;    
  13.     Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);    
  14.     newOpts.inJustDecodeBounds = false;    
  15.     int w = newOpts.outWidth;    
  16.     int h = newOpts.outHeight;    
  17.     //现在主流手机比较多是800*480分辨率,所以高和宽我们设置为    
  18.     float hh = 800f;//这里设置高度为800f    
  19.     float ww = 480f;//这里设置宽度为480f    
  20.     //缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可    
  21.     int be = 1;//be=1表示不缩放    
  22.     if (w > h && w > ww) {//如果宽度大的话根据宽度固定大小缩放    
  23.         be = (int) (newOpts.outWidth / ww);    
  24.     } else if (w < h && h > hh) {//如果高度高的话根据宽度固定大小缩放    
  25.         be = (int) (newOpts.outHeight / hh);    
  26.     }    
  27.     if (be <= 0)    
  28.         be = 1;    
  29.     newOpts.inSampleSize = be;//设置缩放比例    
  30.     //重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了    
  31.     isBm = new ByteArrayInputStream(baos.toByteArray());    
  32.     bitmap = BitmapFactory.decodeStream(isBm, null, newOpts);    
  33.     return compressImage(bitmap);//压缩好比例大小后再进行质量压缩    
  34. }    

Android 设置壁纸功能 :

在Android中设置壁纸的方法有三种,分别是:

            1、使用WallpaperManager的setResource(int ResourceID)方法

            2、使用WallpaperManager的setBitmap(Bitmap bitmap)方法

            3、重写ContextWrapper 类中提供的setWallpaper()

            除此之外,我们还需要在应用程序中加入下列权限: <uses-permission android:name="android.permission.SET_WALLPAPER"/>

            下面我们以此为基本方法,来实现Android中自带的壁纸应用。首先来看我的布局代码:

[html]  view plain copy print ?
  1. <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"  
  2.     xmlns:tools="http://schemas.android.com/tools"  
  3.     android:layout_width="fill_parent"  
  4.     android:layout_height="fill_parent"  
  5.     android:orientation="vertical"  
  6.     android:background="#000000"  
  7.     tools:context=".MainActivity" >  
  8.     <ImageSwitcher  
  9.         android:id="@+id/ImageSwitcher"  
  10.         android:layout_width="fill_parent"  
  11.         android:layout_height="370dp">  
  12.     </ImageSwitcher>  
  13.     <Gallery  
  14.         android:id="@+id/Gallery"  
  15.         android:layout_width="fill_parent"  
  16.         android:layout_height="80dp"  
  17.         android:layout_below="@+id/ImageSwitcher"  />  
  18.     <Button  
  19.         android:id="@+id/BtnGo"  
  20.         android:layout_width="wrap_content"  
  21.         android:layout_height="40dp"  
  22.         android:layout_below="@+id/Gallery"  
  23.         android:layout_alignParentBottom="true"  
  24.         android:layout_centerHorizontal="true"  
  25.         android:text="@string/BtnGo" />  
  26. </RelativeLayout>  
           在这里我们使用Gallery来实现一个可以供用户选择的缩略图列表,当用户选择列表中的图像时,会在ImageSwitcher控件中显示出当前图像,当点击Button时,当前图片将被设置为壁纸。其实这里的ImageSwitcher完全可以替换为ImageView,考虑到ImageSwitcher可以提供较好的动画效果,所以我们在这里选择了ImageSwitcher。同样地,我们继续使用 Android开发学习之Gallery中的那个ImageAdapter类:

[java]  view plain copy print ?
  1. package com.android.gallery2switcher;  
  2.   
  3. import android.content.Context;  
  4. import android.view.View;  
  5. import android.view.ViewGroup;  
  6. import android.widget.BaseAdapter;  
  7. import android.widget.ImageView;  
  8.   
  9. public class ImageAdapter extends BaseAdapter{  
  10.   
  11.     //类成员myContext为context父类  
  12.     private Context myContext;  
  13.     private int[] myImages;  
  14.              
  15.     //构造函数,有两个参数,即要存储的Context和Images数组  
  16.     public ImageAdapter(Context c,int[] Images)   
  17.     {  
  18.         // TODO Auto-generated constructor stub  
  19.         this.myContext=c;  
  20.         this.myImages=Images;  
  21.     }  
  22.   
  23.     //返回所有的图片总数量  
  24.     @Override  
  25.     public int getCount()   
  26.     {  
  27.   
  28.         return this.myImages.length;  
  29.     }  
  30.   
  31.     //利用getItem方法,取得目前容器中图像的数组ID  
  32.     @Override  
  33.     public Object getItem(int position)  
  34.     {  
  35.         return position;  
  36.     }  
  37.   
  38.       
  39.     @Override  
  40.     public long getItemId(int position)  
  41.     {  
  42.         return position;  
  43.     }  
  44.   
  45.     //取得目前欲显示的图像的VIEW,传入数组ID值使之读取与成像  
  46.     @Override  
  47.     public View getView(int position, View convertView, ViewGroup parent)   
  48.     {  
  49.         ImageView image=new ImageView(this.myContext);  
  50.         image.setImageResource(this.myImages[position]);  
  51.         image.setScaleType(ImageView.ScaleType.FIT_XY);  
  52.         image.setAdjustViewBounds(true);  
  53.         return image;  
  54.     }  
  55.       
  56. }  
     现在,我们就可以开始编写程序了,后台的代码如下:

[csharp]  view plain copy print ?
  1. package com.android.gallery2switcher;  
  2.   
  3. import java.io.IOException;  
  4.   
  5. import android.os.Bundle;  
  6. import android.app.Activity;  
  7. import android.app.WallpaperManager;  
  8. import android.view.Menu;  
  9. import android.view.View;  
  10. import android.view.View.OnClickListener;  
  11. import android.view.Window;  
  12. import android.view.animation.AnimationUtils;  
  13. import android.widget.AdapterView;  
  14. import android.widget.AdapterView.OnItemSelectedListener;  
  15. import android.widget.Button;  
  16. import android.widget.Gallery;  
  17. import android.widget.Gallery.LayoutParams;  
  18. import android.widget.ImageSwitcher;  
  19. import android.widget.ImageView;  
  20. import android.widget.ViewSwitcher.ViewFactory;  
  21.   
  22. public class MainActivity extends Activity {  
  23.   
  24.     Gallery mGallery;  
  25.     ImageSwitcher mSwitcher;  
  26.     Button BtnGo;  
  27.     int[] Resources=new int[]{R.drawable.image0,R.drawable.image1,R.drawable.image2,R.drawable.image3,  
  28.             R.drawable.image4,R.drawable.image5,R.drawable.image6,R.drawable.image7,R.drawable.image8};  
  29.     int index;  
  30.     @Override  
  31.     protected void onCreate(Bundle savedInstanceState) {  
  32.         super.onCreate(savedInstanceState);  
  33.         //不显示标题栏  
  34.         requestWindowFeature(Window.FEATURE_NO_TITLE);   
  35.         setContentView(R.layout.activity_main);  
  36.         mGallery=(Gallery)findViewById(R.id.Gallery);  
  37.         mSwitcher=(ImageSwitcher)findViewById(R.id.ImageSwitcher);  
  38.         //实现ImageSwitcher的工厂接口  
  39.                 mSwitcher.setFactory(new ViewFactory()  
  40.                 {  
  41.                     @Override  
  42.                     public View makeView()   
  43.                     {  
  44.                           ImageView i = new ImageView(MainActivity.this);  
  45.                           i.setBackgroundColor(0xFF000000);  
  46.                           i.setScaleType(ImageView.ScaleType.FIT_CENTER);  
  47.                           i.setLayoutParams(new ImageSwitcher.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));  
  48.                           return i;  
  49.                     }  
  50.                 });  
  51.         //设置资源  
  52.         mSwitcher.setImageResource(Resources[0]);  
  53.         //设置动画  
  54.         mSwitcher.setInAnimation(AnimationUtils.loadAnimation(this,android.R.anim.fade_in));  
  55.         mSwitcher.setOutAnimation(AnimationUtils.loadAnimation(this,android.R.anim.fade_out));  
  56.         BtnGo=(Button)findViewById(R.id.BtnGo);  
  57.         BtnGo.setOnClickListener(new OnClickListener()  
  58.         {  
  59.             @Override  
  60.             public void onClick(View arg0)   
  61.             {  
  62.                 SetWallPaper();  
  63.             }  
  64.         });  
  65.         ImageAdapter mAdapter=new ImageAdapter(this,Resources);  
  66.         mGallery.setAdapter(mAdapter);  
  67.         mGallery.setOnItemSelectedListener(new OnItemSelectedListener()  
  68.         {  
  69.             @Override  
  70.             public void onItemSelected(AdapterView<?> Adapter, View view,int position, long id)  
  71.             {  
  72.                 //设置图片  
  73.                 mSwitcher.setImageResource(Resources[position]);  
  74.                 //获取当前图片索引  
  75.                 index=position;  
  76.             }  
  77.             @Override  
  78.             public void onNothingSelected(AdapterView<?> arg0)   
  79.             {  
  80.                   
  81.             }  
  82.               
  83.         });  
  84.           
  85.                      
  86.     }  
  87.     //设置壁纸  
  88.     public void SetWallPaper()  
  89.     {  
  90.         WallpaperManager mWallManager=WallpaperManager.getInstance(this);  
  91.         try   
  92.         {  
  93.             mWallManager.setResource(Resources[index]);  
  94.         }   
  95.         catch (IOException e)   
  96.         {  
  97.             e.printStackTrace();  
  98.         }  
  99.     }  
  100.       
  101.     @Override  
  102.     public boolean onCreateOptionsMenu(Menu menu)   
  103.     {  
  104.         return true;  
  105.     }  
  106.   
  107. }  
      可以看到,在使用ImageSwitcher的时候,我们需要实现它的工厂接口,并且这里的makeView()方法和BaseAdapter里的getView()方法是一样的,即返回一个View视图。我们ImageSwitcher给使用了系统默认的动画效果。最终运行效果如下:



你可能感兴趣的:(传递Bitmap + 图片压缩处理 并保存 + 壁纸设置 总结)