我们很多时候需要进行图片的裁剪,其实这个功能在android系统中已经有一套解决方案了,虽然界面和效果并不是很优秀但功能毫无疑问是完美实现了。至于,不用自带的方案怎么做自定义,这个就是后话了。本篇主要讲解的是裁剪的原理和流程,外带分析了大图裁剪和小图裁剪的不同之处,同时给出具体的实现方案。
一、原理+流程
andorid提供了一个action,com.android.camera.action.CROP,
是Intent intent = new Intent("com.android.camera.action.CROP");
通过这个action就可以实现图片的裁剪,具体就是实现这个intent,然后在这个intent中putExtra()中put各种参数,最后通过来启动一个startActivityForResult(intent, requestCode);,这是裁剪图片的activity,进行裁剪。
裁剪完后返回一个bitmap,交给开发者进行处理。
也就是说,我们是通过系统写好的Activity进行了主要的操作,自己只需要在activity类中的onActivityResult中根据requestCode来进行判断和处理即可。
启动系统相册的Activity:
/** * 从相册获取图片 */ private void choicePicFromAlbum() { // 来自相册 Intent albumIntent = new Intent(Intent.ACTION_PICK, null); /** * 下面这句话,与其它方式写是一样的效果,如果: * intent.setData(MediaStore.Images.Media.EXTERNAL_CONTENT_URI); * intent.setType(""image/*");设置数据类型 * 要限制上传到服务器的图片类型时可以直接写如:"image/jpeg 、 image/png等的类型" */ albumIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); startActivityForResult(albumIntent, ALBUM_OK); }
启动系统照相的Activity:
/** * 拍照后获取图片 */ private void choicePicFromCamera() { // 来自相机 Intent cameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 下面这句指定调用相机拍照后的照片存储的路径,这样通过这个uri就可以得到这个照片了 cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); startActivityForResult(cameraIntent, CAMERA_OK);// CAMERA_OK是用作判断返回结果的标识 }
启动裁剪图片的Activity:
/** * 裁剪图片方法实现 * @param uri */ public void clipPhoto(Uri uri) { Intent intent = new Intent("com.android.camera.action.CROP"); //可以选择图片类型,如果是*表明所有类型的图片 intent.setDataAndType(uri, "image/*"); // 下面这个crop = true是设置在开启的Intent中设置显示的VIEW可裁剪 intent.putExtra("crop", "true"); // aspectX aspectY 是宽高的比例,这里设置的是正方形(长宽比为1:1) intent.putExtra("aspectX", 1); intent.putExtra("aspectY", 1); // outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 1000); intent.putExtra("outputY", 1000); //裁剪时是否保留图片的比例,这里的比例是1:1 intent.putExtra("scale", true); //是否是圆形裁剪区域,设置了也不一定有效 //intent.putExtra("circleCrop", true); //设置输出的格式 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); //是否将数据保留在Bitmap中返回 intent.putExtra("return-data", true); startActivityForResult(intent, CUT_OK); }
二、主要问题
如果我们截取的图片是大图,那么我们首先会想着提高输出图片的大小
// outputX outputY 是裁剪图片宽高 intent.putExtra("outputX", 800); intent.putExtra("outputY", 800);
但这样就会出现问题,由于图片过大,占用内存过多所以系统会自行将图片进行压缩,以避免出现OOM的问题。下面摘录一篇博文的部分内容来解释这个问题:
原文:http://blog.csdn.net/floodingfire/article/details/8144604
在Android中,Intent触发Camera程序,拍好照片后,将会返回数据,但是考虑到内存问题,Camera不会将全尺寸的图像返回给调用的Activity,一般情况下,有可能返回的是缩略图,比如120*160px。
这是为什么呢?这不是一个Bug,而是经过精心设计的,却对开发者不透明。以我的小米手机为例,摄像头800W像素,根据我目前设置拍出来的图片尺寸为3200*2400px。有人说,那就返回呗,大不了耗1-2M的内存,不错,这个尺寸的图片确实只有1.8M左右的大小。但是你想不到的是,这个尺寸对应的Bitmap会耗光你应用程序的所有内存。Android出于安全性考虑,只会给你一个寒碜的缩略图。
在Android2.3中,默认的Bitmap为32位,类型是ARGB_8888,也就意味着一个像素点占用4个字节的内存。我们来做一个简单的计算题:3200*2400*4 bytes = 30M。
如此惊人的数字!哪怕你愿意为一张生命周期超不过10s的位图愿意耗费这么巨大的内存,Android也不会答应的。
1 | Mobile devices typically have constrained system resources. |
2 | Android devices can have as little as 16MB of memory available to a single application. |
这是Android Doc的原文,虽然不同手机系统的厂商可能围绕16M这个数字有微微的上调,但是这30M,一般的手机还真挥霍不起。也只有小米这种牛机,内存堪比个人PC,本着土财主般挥金如土的霸气才能做到。
得出的结论是,如果你截取的是小图那么就返回一个bitmap,但如果是大图那么就返回一个uri。这样就不会出现问题啦。
三、从相册中截图
参考自:http://blog.csdn.net/floodingfire/article/details/8144615
现在原作者托管的代码已经用了lib包作为例子了,和博客中略有差异。
原作者博文中写了两种方式,我个人用的不是很习惯。通过自己的测试发现博主提供的方法在手机上也没法适用,于是贴出自己的解决方案。目前在小米2-原生4.4系统上测试通过
具体写法参照:
http://www.cnblogs.com/tianzhijiexian/p/3989296.html
这个文章是参照博主的写的,在miui上测试通过:http://www.cnblogs.com/tianzhijiexian/p/3859480.html,但之后就出问题了。