我们很多时候需要进行图片的裁剪,其实这个功能在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,交给开发者进行处理。

Android 根据照片裁切一个圆形头像 android图片裁剪_移动开发

也就是说,我们是通过系统写好的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的问题。下面摘录一篇博文的部分内容来解释这个问题:

原文:

  在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也不会答应的。


Mobile devices typically have constrained system resources.

	Android devices can have as little as 16MB of memory available to a single application.


    这是Android Doc的原文,虽然不同手机系统的厂商可能围绕16M这个数字有微微的上调,但是这30M,一般的手机还真挥霍不起。也只有小米这种牛机,内存堪比个人PC,本着土财主般挥金如土的霸气才能做到。

 

得出的结论是,如果你截取的是小图那么就返回一个bitmap,但如果是大图那么就返回一个uri。这样就不会出现问题啦。

Android 根据照片裁切一个圆形头像 android图片裁剪_宽高_02

 

三、从相册中截图

参考自:

现在原作者托管的代码已经用了lib包作为例子了,和博客中略有差异。

原作者博文中写了两种方式,我个人用的不是很习惯。通过自己的测试发现博主提供的方法在手机上也没法适用,于是贴出自己的解决方案。目前在小米2-原生4.4系统上测试通过

 具体写法参照:


这个文章是参照博主的写的,在miui上测试通过:,但之后就出问题了。