Android-圖片的選擇,裁剪,壓縮,適配高版本
趁著國慶有時間我們來聊一聊最常用的選取使用者圖片一系列的功能,go!
效果展示
ofollow,noindex">效果展示連結1.圖片的選取
拍照
我們之前設定拍照儲存的檔案地址的Uri,都是直接Intent.putExtra(MediaStore.EXTRA_OUTPUT,檔案儲存的Uri路徑),但是7.0之後,對使用者許可權提高了保護,之前那種方式行不通了,所以我們要做7.0的判斷,用FileProvider獲取設定儲存的檔案Uri,然後放到Intent.putExtra(MediaStore.EXTRA_OUTPUT,檔案儲存的Uri路徑)中,程式碼如下:
//相機拍照的一個標識,後面用 TAKEPAHTO = 1; // 啟動系統相機 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //7.0以下設定儲存圖片的地址 Uri norTakePhotoSaveAdr; // 判斷7.0android系統 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { //臨時新增一個拍照許可權 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); ////通過FileProvider獲取儲存圖片file的uri(先建立file,然後獲取File的Uri) takePhotoSaveAdr = FileProvider.getUriForFile(MainActivity.this, "com.hxzk.bj.photodemo", new File(Environment.getExternalStorageDirectory(), "savephoto.jpg")); //MediaStore.EXTRA_OUTPUT-此設定需要一個儲存視訊的路徑和檔名的Uri intent.putExtra(MediaStore.EXTRA_OUTPUT, takePhotoSaveAdr); } else { norTakePhotoSaveAdr = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "savephoto.jpg")); intent.putExtra(MediaStore.EXTRA_OUTPUT, norTakePhotoSaveAdr); } //PHOTO_TAKEPHOTO,相機的一個請求碼,返回時要用 startActivityForResult(intent, PHOTO_TAKEPHOTO);
相簿
相比較拍照,相簿要簡單一點,程式碼中都有註釋,直接看:
//拍照的一個表示 TAKEPAHTO = 0; //呼叫系統圖庫,選擇圖片 //Intent.ACTION_PICK 意思是選擇資料,其具體表達有: // Intent intent = new Intent(Intent.ACTION_GET_CONTENT); //intent.setType("image/*"); 獲取本地圖片 // intent.setType("video/*");獲取本地視訊 //intent.setType("audio/*")獲取本地音樂 // Intent intent = new Intent(Intent.ACTION_PICK); //intent.setType(ContactsContract.Contacts.CONTENT_TYPE); //獲取聯絡人 // startActivityForResult(intent, PICK_CONTACT); //第二種寫法 Intent intent = new Intent(Intent.ACTION_PICK, null); //其中External為sdcard下的多媒體檔案,Internal為system下的多媒體檔案。 //使用INTERNAL_CONTENT_URI只能顯示儲存在內部的照片 intent.setDataAndType( MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*"); //返回結果和標識 startActivityForResult(intent, PHOTO_PHOTOALBUM);
.拍照或選取圖片的返回結果
不論是拍照還是選取圖片,我們都要在Activity的onActivityResult中去獲取返回結果,然後進行下一步操作。這裡提一下,我們在跳轉相簿或拍照都有一個requestCode,如圖:
startActivityForResult(intent, PHOTO_PHOTOALBUM);
這個,我們全域性定義了三個,分別是拍照,相簿,裁剪的標識:
//三個常量全域性標識 //相簿 private static final int PHOTO_PHOTOALBUM = 0; //拍照 private static final int PHOTO_TAKEPHOTO = 1; //裁剪 private static final int PHOTO_PHOTOCLIP = 2;
在onActivityResult中就要用來區分了:

image.png
沒啥說的!
2. 圖片的裁剪和壓縮
圖片的裁剪
圖片的裁剪我們主要看一下starPhotoZoom()這個裁剪方法,程式碼如下:
public void startPhotoZoom(Uri uri) { Log.e("uri=====", "" + uri); //com.android.camera.action.CROP,這個action是呼叫系統自帶的圖片裁切功能 Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(uri, "image/*");//裁剪的圖片uri和圖片型別 intent.putExtra("crop", "true");//設定允許裁剪,如果不設定,就會跳過裁剪的過程,還可以設定putExtra("crop", "circle") intent.putExtra("aspectX", 1);//裁剪框的 X 方向的比例,需要為整數 intent.putExtra("aspectY", 1);//裁剪框的 Y 方向的比例,需要為整數 intent.putExtra("outputX", 60);//返回資料的時候的X畫素大小。 intent.putExtra("outputY", 60);//返回資料的時候的Y畫素大小。 //uritempFile為Uri類變數,例項化uritempFile if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { if (TAKEPAHTO == 1) {//如果是7.0的拍照 //開啟臨時訪問的讀和寫許可權 intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION); //針對7.0以上的操作 intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, uri)); uriClipUri = uri; } else {//如果是7.0的相簿 //設定裁剪的圖片地址Uri uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "clip.jpg"); } } else { uriClipUri = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "clip.jpg"); } Log.e("uriClipUri=====", "" + uriClipUri); //Android 對Intent中所包含資料的大小是有限制的,一般不能超過 1M,否則會使用縮圖 ,所以我們要指定輸出裁剪的圖片路徑 intent.putExtra(MediaStore.EXTRA_OUTPUT, uriClipUri); intent.putExtra("return-data", false);//是否將資料保留在Bitmap中返回 intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());//輸出格式,一般設為Bitmap格式及圖片型別 intent.putExtra("noFaceDetection", true);//人臉識別功能 startActivityForResult(intent, PHOTO_PHOTOCLIP);//裁剪完成的標識 }
圖片的壓縮
我直接上方法吧:
/** * 圖片壓縮的方法 */ public void compressPhto(File mFile){ //BitmapFactory這個類就提供了多個解析方法(decodeResource、decodeStream、decodeFile等)用於建立Bitmap。 //比如如果圖片來源於網路,就可以使用decodeStream方法; //如果是sd卡里面的圖片,就可以選擇decodeFile方法; //如果是資原始檔裡面的圖片,就可以使用decodeResource方法等 BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; // 獲取當前圖片的邊界大小 int outHeight = options.outHeight; int outWidth = options.outWidth; String outMimeType = options.outMimeType; options.inJustDecodeBounds = false; //inSampleSize的作用就是可以把圖片的長短縮小inSampleSize倍,所佔記憶體縮小inSampleSize的平方 options.inSampleSize = caculateSampleSize(options, 10, 10); Bitmap bitmap = BitmapFactory.decodeFile(mFile.getPath(),options); ivUserPhoto.setImageBitmap(bitmap); } /** * 計算出所需要壓縮的大小 * @param options * @param reqWidth * @param reqHeight * @return */ private int caculateSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { int sampleSize = 1; int picWidth = options.outWidth; int picHeight = options.outHeight; if (picWidth > reqWidth || picHeight > reqHeight) { int halfPicWidth = picWidth / 2; int halfPicHeight = picHeight / 2; while (halfPicWidth / sampleSize > reqWidth || halfPicHeight / sampleSize > reqHeight) { sampleSize *= 2; } } return sampleSize; }
3.圖片的適配
7.0適配問題
不知道,大家注意裁剪方法裡的這一段程式碼沒?

image.png
還有拍照的這一段程式碼:

image.png
為什麼要這麼寫呢?
原來7.0一下版本我們,直接呼叫相機獲取的圖片地址是:
file:///storage/emulated/0/temp.jpg的檔案
然而7.0之後就變成:
content://........檔案,使用 content://代替了 file:///
這是因為:Android 為了提高私有檔案的安全性,從 7.0 開始對外傳遞file://型別的uri會觸發FileUriExposedException。因此,在分享私有檔案時必須使用FileProvider。
那麼如果在使用之前的方法就會報錯,我們要給程式在manifest檔案中加入FileProvider:

image.png
我們看看provider_paths這個檔案中都有啥?

image.png
這注解寫的,不會,就轉行吧。
裁剪完成
直接載入圖片顯示

image.png
4.圖片的上傳
上傳圖片
但還有中情況是我們要上傳載入的圖片,我也給大家提供了方法:
Bitmap photoBitmap; File file; /** * 上傳圖片 */ public void upDateFile() { try { //裁剪後的影象轉成BitMap photoBitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uriClipUri)); } catch (FileNotFoundException e) { e.printStackTrace(); } //建立路徑 String path = Environment.getExternalStorageDirectory() .getPath() + "/Pic"; //獲取外部儲存目錄 file = new File(path); //建立新目錄, 建立此抽象路徑名指定的目錄,包括建立必需但不存在的父目錄。 file.mkdirs(); //以當前時間重新命名檔案 long time = System.currentTimeMillis(); //生成新的檔案 file = new File(file.toString() + "/" + time+ ".png"); //建立輸出流 OutputStream out = null; try { out = new FileOutputStream(file.getPath()); } catch (FileNotFoundException e) { e.printStackTrace(); } //壓縮檔案,返回結果,true說明成功了 boolean bCompress = photoBitmap.compress(Bitmap.CompressFormat.JPEG, 100, out); }
4.其他知識點
- 點選使用者頭像彈窗不是我們常用的PupUpWindow,而是自定義的BottomSheetDialog(待優化).
- 使用者頭像被我做成了圓角,這個也是自定義ImageView,大家可以下載檢視原始碼。