做过类似需求的同学都知道,在Activity中通过如下代码可以启动相机,然后在重写的onActivityResult方法中可以获取到返回的照片数据:

IntentopenCameraIntent=newIntent(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.拍照时,将拍得的照片先保存在本地,通过修改之前的代码如下:

Urip_w_picpathUri=Uri.fromFile(newFile(Environment.getExternalStorageDirectory(),"p_w_picpath.jpg"));

//指定照片保存路径(SD卡),p_w_picpath.jpg为一个临时文件,每次拍照后这个图片都会被替换

openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT,p_w_picpathUri);


如何调取相机拍照,代码如下:

/**拍照获取相片**/privatevoiddoTakePhoto(){Intentintent=newIntent(MediaStore.ACTION_IMAGE_CAPTURE);//调用系统相机Urip_w_picpathUri=Uri.fromFile(newFile(Environment.getExternalStorageDirectory(),"p_w_picpath.jpg"));//指定照片保存路径(SD卡),p_w_picpath.jpg为一个临时文件,每次拍照后这个图片都会被替换intent.putExtra(MediaStore.EXTRA_OUTPUT,p_w_picpathUri);//直接使用,没有缩小startActivityForResult(intent,PHOTO_WITH_CAMERA);//用户点击了从相机获取}



2.在onActivityResult方法中再将图片取出,并经过缩小处理再显示在界面上或上传给服务器(压缩比例自定义)

@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){super.onActivityResult(requestCode,resultCode,data);if(resultCode==RESULT_OK){switch(requestCode){caseTAKE_PICTURE://将保存在本地的图片取出并缩小后显示在界面上Bitmapbitmap=BitmapFactory.decodeFile(Environment.getExternalStorageDirectory()+"/p_w_picpath.jpg");  BitmapnewBitmap=zoomBitmap(bitmap,bitmap.getWidth()/SCALE,bitmap.getHeight()/SCALE);//由于Bitmap内存占用较大,这里需要回收内存,否则会报outofmemory异常bitmap.recycle();//将处理过的图片显示在界面上,并保存到本地iv_p_w_picpath.setImageBitmap(newBitmap);savePhotoToSDCard(newBitmap,Environment.getExternalStorageDirectory().getAbsolutePath(),String.valueOf(System.currentTimeMillis()));break;default:break;}}}

由于Android给bitmap分配的内存最大不超过8M,所以对使用完的较大的Bitmap要释放内存,调用其recycle()方法即可。然后将缩小后的bitmap显示在界面上或保存到SD卡,至于之前保存的原图,可以删掉,也可以放在那,下次拍照时,这张原图就会被下一张照片覆盖,所以SD卡上使用只有一张临时图片,占用也不是很大。


以上讲的是拍照获取图片,如果是从相册中获取图片又如何处理呢,我的方法如下:


1.打开相册选取图片:

IntentopenAlbumIntent=newIntent(Intent.ACTION_GET_CONTENT);openAlbumIntent.setType("p_w_picpath/*");startActivityForResult(openAlbumIntent,CHOOSE_PICTURE);


2.在onActivity方法中处理获取到的图片,思路和之前类似

@OverrideprotectedvoidonActivityResult(intrequestCode,intresultCode,Intentdata){super.onActivityResult(requestCode,resultCode,data);if(resultCode==RESULT_OK){switch(requestCode){caseCHOOSE_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_p_w_picpath.setImageBitmap(smallBitmap);}}catch(FileNotFoundExceptione){e.printStackTrace();}catch(IOExceptione){e.printStackTrace();}break;default:break;}}}

还有一个方法 zoomBitmap(),代码如下:

/**缩放Bitmap图片**/publicBitmapzoomBitmap(Bitmapbitmap,intwidth,intheight){intw=bitmap.getWidth();inth=bitmap.getHeight();Matrixmatrix=newMatrix();floatscaleWidth=((float)width/w);floatscaleHeight=((float)height/h);matrix.postScale(scaleWidth,scaleHeight);//利用矩阵进行缩放不会造成内存溢出Bitmapnewbmp=Bitmap.createBitmap(bitmap,0,0,w,h,matrix,true);returnnewbmp;}


至此,功能已实现。


下面是本人项目中所实现的功能在这里总结一下:


1.要想对从图库选择的照片进行裁剪:

/**从相册获取图片**/privateIntentdoPickPhotoFromGallery(){Intentintent=newIntent();intent.setType("p_w_picpath/*");//开启Pictures画面Type设定为p_w_picpathintent.setAction(Intent.ACTION_GET_CONTENT);//使用Intent.ACTION_GET_CONTENT这个Action//实现对图片的裁剪,必须要设置图片的属性和大小intent.setType("p_w_picpath/*");//获取任意图片类型intent.putExtra("crop","true");//滑动选中图片区域intent.putExtra("aspectX",1);//裁剪框比例1:1intent.putExtra("aspectY",1);intent.putExtra("outputX",300);//输出图片大小intent.putExtra("outputY",300);intent.putExtra("return-data",true);//有返回值returnintent;}

调用此方法处:

Intentintent2=doPickPhotoFromGallery();startActivityForResult(intent2,PHOTO_WITH_DATA);

ActivityForResult中和上面一样


2.项目中要拍多少张 就保存多少张,显示图片列表:

A.将拍照的照片或者图库选择的图片,保存到本地

创建图片名,不能重复哦!

/**为图片创建不同的名称用于保存,避免覆盖**/publicstaticStringcreateFileName(){StringfileName="";Datedate=newDate(System.currentTimeMillis());//系统当前时间SimpleDateFormatdateFormat=newSimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");fileName=dateFormat.format(date)+".jpg";returnfileName;}

保存图片到SD卡

/**Savep_w_picpathtotheSDcard**/publicstaticvoidsavePhotoToSDCard(Stringpath,StringphotoName,BitmapphotoBitmap){if(android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)){Filedir=newFile(path);if(!dir.exists()){dir.mkdirs();}FilephotoFile=newFile(path,photoName);//在指定路径下创建文件FileOutputStreamfileOutputStream=null;try{fileOutputStream=newFileOutputStream(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卡上的图片名获取到集合中,在自定义的适配器中根据名字加载图片喽!

自定义图片列表适配器代码:

/***插入图片列表适配器*@authorZHF**/publicclassImagesListAdapterextendsBaseAdapter{privateContextcontext;privateList<String>p_w_picpathsList;//各个图片的路径publicImagesListAdapter(Contextcontext,List<String>p_w_picpathsList){this.context=context;this.p_w_picpathsList=p_w_picpathsList;}/**得到总的数量**/@OverridepublicintgetCount(){//TODOAuto-generatedmethodstubreturnp_w_picpathsList.size();}/**根据ListView位置返回View**/@OverridepublicObjectgetItem(intposition){returnp_w_picpathsList.get(position);//返回当前选中的item图片的路径}/**根据ListView位置得到List中的ID**/@OverridepubliclonggetItemId(intposition){//TODOAuto-generatedmethodstubreturnposition;//返回当前选中项的Id}/**根据位置得到View对象**/@OverridepublicViewgetView(intposition,ViewconvertView,ViewGroupparent){if(convertView==null){convertView=LayoutInflater.from(context).inflate(R.layout.newwrite_p_w_picpath_item,null);}ImageViewimg=(ImageView)convertView.findViewById(R.id.newwrite_et_content_p_w_picpath);//图片列表项if(!p_w_picpathsList.get(position).equals("")){//没有图片BitmaptempBitmap=BitmapFactory.decodeFile(p_w_picpathsList.get(position));//根据路径显示对应的图片BitmapnewBitmap=newImageManager(context).zoomBitmap(tempBitmap,tempBitmap.getWidth(),tempBitmap.getHeight()/3);img.setImageBitmap(newBitmap);//对应的行上显示对应的图片}returnconvertView;}}

ok!完了,在显示图片的时候大家可能会碰到OOM(OutOfMemory)异常,在下一篇博客中我会具体解决了一下~



Demo已上传!下载附件即可!