Android[com.android.camera.action.CROP]剪裁图片详解

Android系统默认提供了action=com.android.camera.action.CROP来调用裁剪图片,具体参数列表如下:

这里展示一下裁剪已有图片文件的函数:

public static Intent buildCropIntent(String action, CropParams params, Uri imageFile) {
    Intent intent = new Intent(action)
            .setDataAndType(imageFile, "image/*")
            .putExtra("aspectX", params.aspectX)
            .putExtra("aspectY", params.aspectY)
            .putExtra("outputX", params.outputX)
            .putExtra("outputY", params.outputY)
            .putExtra("return-data", params.returnData)
            .putExtra("outputFormat", params.outputFormat)
            .putExtra("noFaceDetection", params.noFaceDetection)
            .putExtra("scale", params.scale)
            .putExtra("scaleUpIfNeeded", params.scaleUpIfNeeded)
            .putExtra(MediaStore.EXTRA_OUTPUT, params.uri);
 
    if (params.crop) {
        intent.putExtra("crop", "true");
    }
    return intent;
}

注意imageFile是输入的文件路径,EXTRA_OUTPUT参数对应的,是我们指定的输出文件路径。

在onActivityResult中处理结果:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if (resultCode == Activity.RESULT_OK) {
        // 在return-data=true时,data中会有extra;否则输出到文件
    }
}

很意外的发现,有些情况下params.uri对应的文件长度为0,实测后发现,不同的crop app处理流程不一样,例如Android6.0模拟器自带相册在保存裁剪后图片时,写入到文件是异步的,也就意味着在onActivityResult返回时,裁剪后的image文件还未保存完整。。。然而使用小米手机测试,无论如何它都在裁剪界面转圈保存成功后,才有onActivityResult返回。。。

于是这样带来一个棘手的问题,对这个裁剪后的图片Uri如何处理?不过,我发现大家好像都使用了另一段代码来工作正常:

private Bitmap decodeUriAsBitmap(Uri uri){
    Bitmap bitmap = null;
    try {
        bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        return null;
    }
    return bitmap;
}

真的不可思议,改成这样的方式显示图片,居然工作正常,于是加上日志再看看:

public static Bitmap decodeUriAsBitmap(Context context, Uri uri) {
    if (context == null || uri == null) return null;
 
    Bitmap bitmap;
    try {
        InputStream is = context.getContentResolver().openInputStream(uri);
        try {
            Log.w(TAG, "decodeUriAsBitmap Before decode is.length = " + is.available());
        } catch (IOException e) {
            e.printStackTrace();
        }
        bitmap = BitmapFactory.decodeStream(is);
        try {
            Log.w(TAG, "decodeUriAsBitmap After decode is.length = " + is.available());
        } catch (IOException e) {
           e.printStackTrace();
        }
    } catch (FileNotFoundException e) {
        e.printStackTrace();
        return null;
    }
    return bitmap;
}

观察日志输出:
decodeUriAsBitmap Before decode is.length = 12165
decodeUriAsBitmap After decode is.length = 0
但也存在少数Before情况=0的时候,看来通过context.getContentResolver().openInputStream(uri)得到的InputStream的确好用。

为了保险起见,在使用裁剪得到的Uri时,需要线程异步等待io完成后,再使用该文件;
这里采用判断文件长度的方式来决定文件是否可用:

public static boolean isPhotoReallyCropped(Uri uri) {
    File file = new File(uri.getPath());
    long length = file.length();
    Log.w(TAG, "isPhotoReallyCropped uri = " + uri.getPath() + ", length = " + length);
    return length > 0;
}

线程异步判断:

private void checkInputFileLength() throws IOException {
    // 检查输入文件是否存在,如果不存在,是否等待超时
    if (millionSecondTimeOut > 0) {
        File f = new File(getPath());
        if (!f.exists()) throw new FileNotFoundException();
        if (f.length() == 0) {
            long time = System.currentTimeMillis();
            long time2 = time;
            while (f.length() == 0) {
                try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                    return;
                }
                time2 = System.currentTimeMillis();
                if (time2 - time > millionSecondTimeOut)
                    break;
            }
            time2 = System.currentTimeMillis();
            Log.w("Network", "checkInputFileLength cost: " + (time2-time));
            if (f.length() == 0) throw new FileNotFoundException();
        }
    }
}

参考文章:
http://www.linuxidc.com/Linux/2012-11/73939.htm
http://www.linuxidc.com/Linux/2012-11/73940.htm

扩展阅读,自定义实现裁剪界面:
https://github.com/edmodo/cropper