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

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

bitmap转化为byte数组的方法:

private byte[] Bitmap2Bytes(Bitmap bm){     
    ByteArrayOutputStream baos = new ByteArrayOutputStream();       
    bm.compress(Bitmap.CompressFormat.PNG, 100, baos);       
    return baos.toByteArray();     
   } 
byte数组转化为bitmap方法:
byte buff[]=mIntent.getByteArrayExtra("image");  
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是,它占用的内存并没有改变  

public static void compressBmpToFile(Bitmap bmp,File file){
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		int options = 80;//个人喜欢从80开始,
		bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
		while (baos.toByteArray().length / 1024 > 100) { 
			baos.reset();
			options -= 10;
			bmp.compress(Bitmap.CompressFormat.JPEG, options, baos);
		}
		try {
			FileOutputStream fos = new FileOutputStream(file);
			fos.write(baos.toByteArray());
			fos.flush();
			fos.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

方法说明: 该方法是压缩图片的质量, 注意它不会减少图片的像素,比方说, 你的图片是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形式的图片, 后果可想而知

private Bitmap compressBmpFromBmp(Bitmap image) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		int options = 100;
		image.compress(Bitmap.CompressFormat.JPEG, 100, baos);
		while (baos.toByteArray().length / 1024 > 100) { 
			baos.reset();
			options -= 10;
			image.compress(Bitmap.CompressFormat.JPEG, options, baos);
		}
		ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());
		Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);
		return bitmap;
	}
  再看一个方法:
	private Bitmap compressImageFromFile(String srcPath) {
		BitmapFactory.Options newOpts = new BitmapFactory.Options();
		newOpts.inJustDecodeBounds = true;//只读边,不读内容
		Bitmap bitmap = BitmapFactory.decodeFile(srcPath, newOpts);

		newOpts.inJustDecodeBounds = false;
		int w = newOpts.outWidth;
		int h = newOpts.outHeight;
		float hh = 800f;//
		float ww = 480f;//
		int be = 1;
		if (w > h && w > ww) {
			be = (int) (newOpts.outWidth / ww);
		} else if (w < h && h > hh) {
			be = (int) (newOpts.outHeight / hh);
		}
		if (be <= 0)
			be = 1;
		newOpts.inSampleSize = be;//设置采样率
		
		newOpts.inPreferredConfig = Config.ARGB_8888;//该模式是默认的,可不设
		newOpts.inPurgeable = true;// 同时设置才会有效
		newOpts.inInputShareable = true;//。当系统内存不够时候图片自动被回收
		
		bitmap = BitmapFactory.decodeFile(srcPath, newOpts);
//		return compressBmpFromBmp(bitmap);//原来的方法调用了这个方法企图进行二次压缩
									//其实是无效的,大家尽管尝试
		return bitmap;
	}

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

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

private Bitmap compressImage(Bitmap image) {  
  
        ByteArrayOutputStream baos = new ByteArrayOutputStream();  
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//质量压缩方法,这里100表示不压缩,把压缩后的数据存放到baos中  
        int options = 90;  
        while ( baos.toByteArray().length / 1024>100) {  //循环判断如果压缩后图片是否大于100kb,大于继续压缩         
            baos.reset();//重置baos即清空baos  
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//这里压缩options%,把压缩后的数据存放到baos中  
            options -= 10;//每次都减少10  
        }  
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把压缩后的数据baos存放到ByteArrayInputStream中  
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream数据生成图片  
        return bitmap;  
    } 

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

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

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,内存,服务器,图片,硬盘)