Android7.0适配之图片裁剪

Home / Android MrLee 2016-9-23 3108

Android 7.0系统发布后,拿到能升级的nexus 6P,就开始了7.0的适配。发现Android7.0在修改头像时候进行拍照并裁剪图片时会出现photos app崩溃。仔细分析操作步骤和流程,发现照片拍照是成功的,SD卡也能保存相关的图片信息,但是在对拍照的图片进行裁剪时候出现了photos app崩溃;如下图:

Android7.0适配之图片裁剪

Android7.0适配之图片裁剪

同时发现通过选择相册进行选中图片后才进行裁剪就没有问题。看一下代码:

Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(Uri.fromFile(inputfile), IMAGE_UNSPECIFIED);//主要问题就在这个File Uri上面  ————代码语句A
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 180);
intent.putExtra("outputY", 180);
intent.putExtra("scale", true);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputfile));//定义输出的File Uri,之后根据这个Uri去拿裁剪好的图片信息  ————代码B
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true); 
startActivityForResult(intent, RequestCode);

上网查找了一些资料都是说Android7.0后,发现Android7.0在这个方面最大变化就在于以前适用的File Uri需要更改为Content Uri,但是这个更改是需要编译的 targetSdkVersion 是24的时候才有效,我们的apptargetSdkVersion是23。所以问题上面的链接解决办法应该不是这个原因(这个解决在后文解决 targetSdkVersion=24 的时候需要用到);

但是从文章中知道了File Uri和Content Uri的区别,然后再去分析一下为什么从相册选择图片进行裁剪是生效的?通过代码分析和debug后发现,从相册选取图片得到的Uri是Content Uri而拍照后使用的是文件路径生成的File Uri,看来问题就是出在这里,并不是说我们app的targetSDKVersion不是24就可以使用File Uri,但是photos app的targetSdkVersion可能是24导致了它接受了File Uri而崩溃,那么我们需要做的就是把File Uri换成Content Uri。这里需要提的是,直接按照 这里的做法 去更换Content Uri并不能生效,会提示“Can not edit image under 50*50 pixels”的错误toast提示,其实是photos app找不到Content Uri传进去的图片文件。那么我们需要换一种方式去更换Content Uri,我们在 stackoverflow 上面找到更换Content Uri的方法,需要注意的是不是所有的File Uri都可以转换成Content Uri,应该是多媒体相关的文件才可以。下面是代码:

public static Uri getImageContentUri(Context context, File imageFile) {
    String filePath = imageFile.getAbsolutePath();
    Cursor cursor = context.getContentResolver().query(
            MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
            new String[] { MediaStore.Images.Media._ID },
            MediaStore.Images.Media.DATA + "=? ",
            new String[] { filePath }, null);
    if (cursor != null && cursor.moveToFirst()) {
        int id = cursor.getInt(cursor
                .getColumnIndex(MediaStore.MediaColumns._ID));
        Uri baseUri = Uri.parse("content://media/external/images/media");
        return Uri.withAppendedPath(baseUri, "" + id);
    } else {
        if (imageFile.exists()) {
            ContentValues values = new ContentValues();
            values.put(MediaStore.Images.Media.DATA, filePath);
            return context.getContentResolver().insert(
                    MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
        } else {
            return null;
        }
    }
}

我们需要把上文第一处代码的Uri.fromFile(inputfile)更换成getImageContentUri生成的Uri。替换上去后运行程序试试,这样修改就可以了。

更进一步:要是我们把上文的代码,把app的targetSdkVersion改成24之后,在启动相机进行拍照时候,会发现这样的错误

android.os.FileUriExposedException: file:///storage/emulated/0/DCIM/Camera/TEMP_IMAGE1474468182889.jpg exposed beyond app through ClipData.Item.getUri();

不能拍照成功;使用FileProvider来产生Content Uri代替File Uri,按照上面网址介绍方法替换掉就可以;

然而我们在用FileProvider.getUriForFile替换掉所有的Uri.fromFile时候,可以拍照成功了,但是在剪裁图片时候还是会出现之前的“Can not edit image under 50*50 pixels”没有办法,只能把上文的代码语句A重新更改为getImageContentUri生成Content Uri,重新运行程序;这个时候可以拍照成功,进入图片裁剪photos app里面,但是裁剪完成Save的时候photos app又崩溃了:

Android7.0适配之图片裁剪

应该是FileProvider的属性为android:exported="false"的原因,但是这里不能改为true(会报另一个错误:java.lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.SecurityException: Provider must not be exported)。

这个时候我们需要把代码语句B里面outputUri改为File Uri就可以了,最终的代码如下,调用相机拍照的代码自己替换成FileProvider就可以了:

Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(getImageContentUri(context , inputfile), IMAGE_UNSPECIFIED);//自己使用Content Uri替换File Uri
intent.putExtra("crop", "true");
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
intent.putExtra("outputX", 180);
intent.putExtra("outputY", 180);
intent.putExtra("scale", true);
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputfile));//定义输出的File Uri
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
startActivityForResult(intent, RequestCode);

综上,我们完成了targetSdkVersion=24和小于24两种情况的图片裁剪适配;之后还是采用自己app内程序进行图片裁剪适配性比较好。

至于为什么Google需要对Content Uri和File Uri使用进行更改,应该是希望限制开发者对存储空间media文件的访问控制;

来自:http://www.jianshu.com/p/c73b959b6bcf





本文链接:https://www.it72.com/10473.htm

推荐阅读
最新回复 (0)
返回