Android7.0适配之图片裁剪

WarrenEPK 8年前
   <p>Android 7.0系统发布后,拿到能升级的nexus 6P,就开始了7.0的适配。发现Android7.0在修改头像时候进行拍照并裁剪图片时会出现photos app崩溃。仔细分析操作步骤和流程,发现照片拍照是成功的,SD卡也能保存相关的图片信息,但是在对拍照的图片进行裁剪时候出现了photos app崩溃;如下图:</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/a59e572098a2939c5b032999d372f913.png"></p>    <p><img src="https://simg.open-open.com/show/b951a7a77597bb6d9df1ed789e483bd5.png"></p>    <p>同时发现通过选择相册进行选中图片后才进行裁剪就没有问题。看一下代码:</p>    <pre>  <code class="language-java">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);</code></pre>    <p>上网查找了一些资料都是说Android7.0后,发现Android7.0在这个方面最大变化就在于以前适用的File Uri需要更改为Content Uri,但是这个更改是需要编译的 <strong>targetSdkVersion</strong> 是24的时候才有效,我们的apptargetSdkVersion是23。所以问题上面的链接解决办法应该不是这个原因(这个解决在后文解决 <strong>targetSdkVersion=24</strong> 的时候需要用到);</p>    <p>但是从文章中知道了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。这里需要提的是,直接按照 <a href="/misc/goto?guid=4959716885133284240" rel="nofollow,noindex">这里的做法</a> 去更换Content Uri并不能生效,会提示“Can not edit image under 50*50 pixels”的错误toast提示,其实是photos app找不到Content Uri传进去的图片文件。那么我们需要换一种方式去更换Content Uri,我们在 <a href="/misc/goto?guid=4959716885227695008" rel="nofollow,noindex">stackoverflow</a> 上面找到更换Content Uri的方法,需要注意的是不是所有的File Uri都可以转换成Content Uri,应该是多媒体相关的文件才可以。下面是代码:</p>    <pre>  <code class="language-java">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;          }      }  }</code></pre>    <p>我们需要把上文第一处代码的Uri.fromFile(inputfile)更换成getImageContentUri生成的Uri。替换上去后运行程序试试,这样修改就可以了。</p>    <p>更进一步:要是我们把上文的代码,把app的targetSdkVersion改成24之后,在启动相机进行拍照时候,会发现这样的错误</p>    <p>android.os.FileUriExposedException: file:///storage/emulated/0/DCIM/Camera/TEMP_IMAGE1474468182889.jpg exposed beyond app through ClipData.Item.getUri();</p>    <p>不能拍照成功;使用FileProvider来产生Content Uri代替File Uri,按照上面网址介绍方法替换掉就可以;</p>    <p>然而我们在用FileProvider.getUriForFile替换掉所有的Uri.fromFile时候,可以拍照成功了,但是在剪裁图片时候还是会出现之前的“Can not edit image under 50*50 pixels”没有办法,只能把上文的代码语句A重新更改为getImageContentUri生成Content Uri,重新运行程序;这个时候可以拍照成功,进入图片裁剪photos app里面,但是裁剪完成Save的时候photos app又崩溃了:</p>    <p><img src="https://simg.open-open.com/show/3c49cb28f378561ed7b0d6a0c1cc70d7.png"></p>    <p>应该是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)。</p>    <p>这个时候我们需要把代码语句B里面outputUri改为File Uri就可以了,最终的代码如下,调用相机拍照的代码自己替换成FileProvider就可以了:</p>    <pre>  <code class="language-java">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);</code></pre>    <p>综上,我们完成了targetSdkVersion=24和小于24两种情况的图片裁剪适配;之后还是采用自己app内程序进行图片裁剪适配性比较好。</p>    <p>至于为什么Google需要对Content Uri和File Uri使用进行更改,应该是希望限制开发者对存储空间media文件的访问控制;</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/c73b959b6bcf</p>    <p> </p>