Android7.0適配之圖片裁剪

WarrenEPK 9年前發布 | 141K 次閱讀 安卓開發 Android開發 移動開發

Android 7.0系統發布后,拿到能升級的nexus 6P,就開始了7.0的適配。發現Android7.0在修改頭像時候進行拍照并裁剪圖片時會出現photos app崩潰。仔細分析操作步驟和流程,發現照片拍照是成功的,SD卡也能保存相關的圖片信息,但是在對拍照的圖片進行裁剪時候出現了photos app崩潰;如下圖:

同時發現通過選擇相冊進行選中圖片后才進行裁剪就沒有問題。看一下代碼:

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又崩潰了:

應該是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

 

 本文由用戶 WarrenEPK 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!