【移动开发】Android相机、相册获取图片显示并保存到SD卡
做过类似需求的同学都知道,在Activity中通过如下代码可以启动相机,然后在重写的onActivityResult方法中可以获取到返回的照片数据: 1 2 IntentopenCameraIntent= new Intent(MediaStore.ACTION_IMAGE_CAPTURE); startActivityForResult(openCameraIntent,TAKE_PICTURE); 在onActivityResult方法里通过Intent的getData方法获取的数据转换成bitmap并显示在界面上,有时候会有取不到数据,或者显示的bitmap会非常小,如果将bitmap保存到sd卡后会发现,图片的分辨率很低,并且图片大小也是经过压缩的,不管将相机的像素设置多高,最后通过这种方式返回的bitmap总是经过压缩了的。如果想获得理想的照片大小和分辨率改如何处理呢? 大家都知道,现在手机像素少则500W或800W,多则4KW(某亚),就拿常见的800W像素的相机拍出来的照片来说,分辨率大概在3200*2400左右,照片大小在2M左右。试想一下,在Android系统中bitmap占用4个字节,3200*2400*4=?,结果大家自己算算,如果为了一张图片,耗用这么大的内存,肯定是不合理的,并且,官方文档中有说明,Android系统分配给每个应用的最大内存是16M,所以,系统为了防止应用内存占用过大,对于在应用内通过相机拍摄的图片最终返回来的结果进行了压缩,压缩后的图片变得很小,通过之前说的getData的方式只能满足比如显示个头像这样的需求。 如果要显示大图,就会出现模糊的情况。那如何获取清晰的大图呢?我的解决思路如下: 1.拍照时,将拍得的照片先保存在本地,通过修改之前的代码如下: 1 UriimageUri=Uri.fromFile( new File(Environment.getExternalStorageDirectory(), "image.jpg" )); //指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换 1 openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); 如何调取相机拍照,代码如下: 1 2 3 4 5 6 7 8 9 10 11 /**拍照获取相片**/ private void doTakePhoto(){ Intentintent= new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //调用系统相机 UriimageUri=Uri.fromFile( new File(Environment.getExternalStorageDirectory(), "image.jpg" )); //指定照片保存路径(SD卡),image.jpg为一个临时文件,每次拍照后这个图片都会被替换 intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); //直接使用,没有缩小 startActivityForResult(intent,PHOTO_WITH_CAMERA); //用户点击了从相机获取 } 2.在onActivityResult方法中再将图片取出,并经过缩小处理再显示在界面上或上传给服务器(压缩比例自定义) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 @Override protected void onActivityResult( int requestCode, int resultCode,Intentdata){ super .onActivityResult(requestCode,resultCode,data); if (resultCode==RESULT_OK){ switch (requestCode){ case TAKE_PICTURE: //将保存在本地的图片取出并缩小后显示在界面上 Bitmapbitmap=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+ "/image.jpg" ); BitmapnewBitmap=zoomBitmap(bitmap,bitmap.getWidth()/SCALE,bitmap.getHeight()/SCALE); //由于Bitmap内存占用较大,这里需要回收内存,否则会报outofmemory异常 bitmap.recycle(); //将处理过的图片显示在界面上,并保存到本地 iv_image.setImageBitmap(newBitmap); savePhotoToSDCard(newBitmap,Environment.getExternalStorageDirectory().getAbsolutePath(), String .valueOf(System.currentTimeMillis())); break ; default : break ; } } } 由于Android给bitmap分配的内存最大不超过8M,所以对使用完的较大的Bitmap要释放内存,调用其recycle()方法即可。然后将缩小后的bitmap显示在界面上或保存到SD卡,至于之前保存的原图,可以删掉,也可以放在那,下次拍照时,这张原图就会被下一张照片覆盖,所以SD卡上使用只有一张临时图片,占用也不是很大。 以上讲的是拍照获取图片,如果是从相册中获取图片又如何处理呢,我的方法如下: 1.打开相册选取图片: 1 2 3 IntentopenAlbumIntent= new Intent(Intent.ACTION_GET_CONTENT); openAlbumIntent.setType( "image/*" ); startActivityForResult(openAlbumIntent,CHOOSE_PICTURE); 2.在onActivity方法中处理获取到的图片,思路和之前类似 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 @Override protected void onActivityResult( int requestCode, int resultCode,Intentdata){ super .onActivityResult(requestCode,resultCode,data); if (resultCode==RESULT_OK){ switch (requestCode){ case CHOOSE_PICTURE: ContentResolverresolver=getContentResolver(); //照片的原始资源地址 UrioriginalUri=data.getData(); try { //使用ContentProvider通过URI获取原始图片 Bitmapphoto=MediaStore.Images.Media.getBitmap(resolver,originalUri); if (photo!= null ){ //为防止原始图片过大导致内存溢出,这里先缩小原图显示,然后释放原始Bitmap占用的内存 BitmapsmallBitmap=zoomBitmap(photo,photo.getWidth()/SCALE,photo.getHeight()/SCALE); //释放原始图片占用的内存,防止outofmemory异常发生 photo.recycle(); iv_image.setImageBitmap(smallBitmap); } } catch (FileNotFoundExceptione){ e.printStackTrace(); } catch (IOExceptione){ e.printStackTrace(); } break ; default : break ; } } } 还有一个方法 zoomBitmap(),代码如下: 1 2 3 4 5 6 7 8 9 10 11 /**缩放Bitmap图片**/ public BitmapzoomBitmap(Bitmapbitmap, int width, int height){ int w=bitmap.getWidth(); int h=bitmap.getHeight(); Matrixmatrix= new Matrix(); floatscaleWidth=((float)width/w); floatscaleHeight=((float)height/h); matrix.postScale(scaleWidth,scaleHeight); //利用矩阵进行缩放不会造成内存溢出 Bitmapnewbmp=Bitmap.createBitmap(bitmap, 0 , 0 ,w,h,matrix, true ); return newbmp; } 至此,功能已实现。 下面是本人项目中所实现的功能在这里总结一下: 1.要想对从图库选择的照片进行裁剪: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 /**从相册获取图片**/ private IntentdoPickPhotoFromGallery(){ Intentintent= new Intent(); intent.setType( "image/*" ); //开启Pictures画面Type设定为image intent.setAction(Intent.ACTION_GET_CONTENT); //使用Intent.ACTION_GET_CONTENT这个Action //实现对图片的裁剪,必须要设置图片的属性和大小 intent.setType( "image/*" ); //获取任意图片类型 intent.putExtra( "crop" , "true" ); //滑动选中图片区域 intent.putExtra( "aspectX" , 1 ); //裁剪框比例1:1 intent.putExtra( "aspectY" , 1 ); intent.putExtra( "outputX" , 300 ); //输出图片大小 intent.putExtra( "outputY" , 300 ); intent.putExtra( "return-data" , true ); //有返回值 return intent; } 调用此方法处: 1 2 Intentintent2=doPickPhotoFromGallery(); startActivityForResult(intent2,PHOTO_WITH_DATA); ActivityForResult中和上面一样 2.项目中要拍多少张 就保存多少张,显示图片列表: A.将拍照的照片或者图库选择的图片,保存到本地 创建图片名,不能重复哦! 1 2 3 4 5 6 7 8 9 /**为图片创建不同的名称用于保存,避免覆盖**/ public static String createFileName(){ String fileName= "" ; Date date= new Date (System.currentTimeMillis()); //系统当前时间 SimpleDateFormatdateFormat= new SimpleDateFormat( "'IMG'_yyyyMMdd_HHmmss" ); fileName=dateFormat.format(date)+ ".jpg" ; return fileName; } 保存图片到SD卡 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 /**SaveimagetotheSDcard**/ public static void savePhotoToSDCard( String path, String photoName, BitmapphotoBitmap){ if (android.os.Environment.getExternalStorageState().equals( android.os.Environment.MEDIA_MOUNTED)){ Filedir= new File(path); if (!dir.exists()){ dir.mkdirs(); } FilephotoFile= new File(path,photoName); //在指定路径下创建文件 FileOutputStreamfileOutputStream= null ; try { fileOutputStream= new FileOutputStream(photoFile); if (photoBitmap!= null ){ if (photoBitmap.compress(Bitmap.CompressFormat.PNG, 100 , fileOutputStream)){ fileOutputStream.flush(); } } } catch (FileNotFoundExceptione){ photoFile. delete (); e.printStackTrace(); } catch (IOExceptione){ photoFile. delete (); e.printStackTrace(); } finally { try { fileOutputStream.close(); } catch (IOExceptione){ e.printStackTrace(); } } } } B.最后就是显示图片列表,因为我们要用到listView,自然少不了Adapter了,我们将保存到SD卡上的图片名获取到集合中,在自定义的适配器中根据名字加载图片喽! 自定义图片列表适配器代码: 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 /** *插入图片列表适配器 *@authorZHF * */ public class ImagesListAdapter extends BaseAdapter{ private Contextcontext; private List< String >imagesList; //各个图片的路径 public ImagesListAdapter(Contextcontext,List< String >imagesList){ this .context=context; this .imagesList=imagesList; } /**得到总的数量**/ @Override public int getCount(){ //TODOAuto-generatedmethodstub return imagesList.size(); } /**根据ListView位置返回View**/ @Override public Object getItem( int position){ return imagesList. get (position); //返回当前选中的item图片的路径 } /**根据ListView位置得到List中的ID**/ @Override public longgetItemId( int position){ //TODOAuto-generatedmethodstub return position; //返回当前选中项的Id } /**根据位置得到View对象**/ @Override public ViewgetView( int position,ViewconvertView,ViewGroupparent){ if (convertView== null ){ convertView=LayoutInflater.from(context).inflate(R.layout.newwrite_image_item, null ); } ImageViewimg=(ImageView)convertView.findViewById(R.id.newwrite_et_content_image); //图片列表项 if (!imagesList. get (position).equals( "" )){ //没有图片 BitmaptempBitmap=BitmapFactory.decodeFile(imagesList. get (position)); //根据路径显示对应的图片 BitmapnewBitmap= new ImageManager(context).zoomBitmap(tempBitmap,tempBitmap.getWidth(),tempBitmap.getHeight()/ 3 ); img.setImageBitmap(newBitmap); //对应的行上显示对应的图片 } return convertView; } } ok!完了,在显示图片的时候大家可能会碰到OOM(OutOfMemory)异常,在下一篇博客中我会具体解决了一下~ Demo已上传!下载附件即可! 本文转自zhf651555765 51CTO博客,原文链接:http://blog.51cto.com/smallwoniu/1248695,如需转载请自行联系原作者