Android拍照相簿裁剪封裝
最近用到從相機/相簿選擇圖片的功能,這個功能雖然不復雜,網上的程式碼也一大堆,但是考慮到可能以後別的地方也會用到,所以就抽個空封裝了一個類,來實現此功能。
先列出來需要解決問題:
- 這個類需要具備哪些功能
至少要支援:相機、相簿、裁剪、裁剪比例設定、縮放功能,因為使用系統的自帶的裁剪功能,所以如裁剪成圓形、裁剪時展示網格、圖片進行二次壓縮等功能暫且不論。
- 如何能夠在下次使用的時候快速整合進去
如果要快速整合,最好是幾行程式碼就可以得到選擇之後的圖片,那麼就需要將彈出相機/相簿選擇框、拍照/選擇相簿之後的裁剪邏輯、圖片儲存邏輯、許可權檢查邏輯、7.0以上Uri邏輯、版本相容問題都在一個類中完成,使用者只需要設定開關引數即可。
- 6.0以上許可權問題
6.0以上動態檢查許可權問題、設定了targetSdkVersion設定為23以下時正常的許可權檢查無效問題,沒有許可權時自動獲取許可權。
- 7.0以上Uri問題
在7.0版本以上系統設定不能直接使用本地真是路徑的Uri,必須使用FileProvider封裝之後才可以獲取,但是這個也是分情況的,具體下面會提到。
- 重複選擇拍照時產生的垃圾檔案問題
選定一張圖片後,覺得不好重新由選擇了一張,之前的那個圖片檔案怎麼處理,如果加入裁剪功能,那麼裁剪後拍照的原圖怎麼處理。如果像發朋友圈一樣需要連續多張圖片時,產生的檔案會不會衝突?
展示效果
先上效果圖,然後說程式碼:

image.png
注意事項
根據前面提到的幾個問題,有以下幾點需要說明:
-
封裝一個類,傳入Activity上下文(沒有使用content是因為需要開啟相機、相簿等intent需要startactiity,雖然fragment也可以,但是這樣在純activity中就無法使用了),提供是否裁剪、裁剪比例、裁剪輸出尺寸、是否縮放的開關方法,並提供預設值,可以在使用過程中自行改動。使用
PopupWindow
展示選擇框(當然也可以使用dialog),這樣可以自由控制選擇框的。 -
彈出選擇框時檢查許可權,如果沒有許可權則進行許可權申請,如果使用者點選了已拒絕授權,彈出設定介面,引導使用者授權,授權成功後彈出選擇框。
-
提供回撥介面,在獲取圖片成功之後通過回撥返回圖片的路徑。
-
相機拍照、相簿選擇之後、裁剪之後、許可權回撥需要通過
onActivityResult
和onRequestPermissionsResult
來實現,所以使用的地方需要將這兩個方法傳遞給封裝的類。 -
前面提到過的7.0以上Uri問題,使用
FileProvider
來實現。但是如果專案的targetSdkVersion
設定成23以下的話,即使在7.0以上手機上,也可以不用FileProvider
的形式來獲取Uri,不過targetSdkVersion
在23以下,在小米市場無法上傳安裝包(目前我就知道這個市場) -
在4.4開始,從相簿選擇照片返回的Uri就不能直接使用,所以需要經過轉化才能得到真實的圖片路徑。
-
7.0以上,
targetSdkVersion
設定為23以上時,相機拍照輸出的uri需要通過FileProvider
獲取的,否則會丟擲異常。裁剪輸入的uri為普通形式獲取的uri,裁剪輸出的uri需要時通過FileProvider
轉換的uri,要不會提示無法載入圖片/無法儲存裁剪的圖片。 -
圖片裁剪時,可以選擇直接
return-data
true或者false,true則直接返回對應的bitmap,false需要制定一個輸出uri,為了記憶體考慮,在封裝時,選擇false,讓其返回對應的uri( 需要考慮targetSdkVersion
和7.0以上的uri問題) 。 -
在
targetSdkVersion
設定為23以下時,從相機拍照時會生成一個圖片,可以得到一個對應的uri,如果需要裁剪,可以繼續使用該uri,系統會自動替代之前的拍照生成的檔案,這樣減少了無用檔案。但是設定targetSdkVersion
大於23,且在高版本(這個沒有具體研究是哪一個版本,我測試的是華為8.1)使用同一個uri裁剪時,會丟擲一個已存在的錯誤。所以封裝類中相機拍照後裁剪輸入和輸出的uri處理成不同的,裁剪完成後再刪除拍照時生成的圖片。 -
檔案的命名使用當前時間戳,儲存在sd卡下面以專案包名為名的資料夾下。
-
為了方便程式碼整合,一些處理圖片、處理uri的公共方法,也封裝到一個類中,如果覺得不合適可以自行單獨出來一個工具類(或者自己的專案中已經有對應的方法就可以刪掉對應的程式碼)。
-
在fragment中使用時,因為傳遞的是activity,所以onActivityResult等方法是fragment對應的activity先接收到,需要activity傳遞給fragment在傳遞到這個類中。
-
程式碼中有詳細的註釋
程式碼
- 主要的類(SelectPictureManager.java)
import android.Manifest; import android.app.Activity; import android.content.ContentUris; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.Build; import android.os.Environment; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.support.v4.app.ActivityCompat; import android.support.v4.content.FileProvider; import android.support.v4.content.PermissionChecker; import android.text.TextUtils; import android.view.Gravity; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.WindowManager; import android.widget.Button; import android.widget.PopupWindow; import android.widget.Toast; import java.io.File; import java.io.IOException; /** * 管理選擇相簿的類 * Application * Created by anonyper on 2018/11/19. */ public class SelectPictureManager { /* 定義返回的code值 */ public static final int TAKE_PHOTO_CODE = 1000;//拍照 public static final int CHOOSE_PHOTO_CODE = 2000;//選擇相簿 public static final int PICTURE_CROP_CODE = 3000;//剪下圖片 public static final int REQUEST_PERMISSIONS = 4000;//授權code private boolean isNeedCrop = false;//是否需要裁剪 預設不需要 private boolean isScale = true;//是否需要支援縮放 在可裁剪情況下有效 private boolean isContinuous = false;//是否是連拍模式 比如連續需要兩張以上照片 private String fileName;//檔名字 private String oldFileName;//拍照裁剪時的原圖片 需要刪掉 private Uri outImageUri;//相機拍照圖片儲存地址 private int aspectX = 1000;//裁剪的寬高比例 private int aspectY = 1001;//裁剪的寬高比例 兩個比例略微不一樣是為了解決部分手機1:1時顯示的時圓形裁剪框 private int outputX = 400;//裁剪後輸出圖片的尺寸大小 private int outputY = 400;//裁剪後輸出圖片的尺寸大小 Activity activity;//全域性上下文 需要startactiity PictureSelectListner pictureSelectListner;//選擇之後照片的回撥監聽 /** * 構造SelectPictureManager物件 * * @param activity 上下文 這樣就可以自主的實現 */ public SelectPictureManager(Activity activity) { this(activity, null); } /** * 構造SelectPictureManager物件 * * @param activity上下文 * @param pictureSelectListner 回撥監聽 */ public SelectPictureManager(Activity activity, PictureSelectListner pictureSelectListner) { this.activity = activity; this.pictureSelectListner = pictureSelectListner; fileName = System.currentTimeMillis() + ".jpg";//預設使用時間戳作為圖片名字 } /** * 設定圖片回撥監聽 * * @param pictureSelectListner */ public void setPictureSelectListner(PictureSelectListner pictureSelectListner) { this.pictureSelectListner = pictureSelectListner; } /** * 設定連拍 這樣可以連續排多張不重複的圖片 * * @param isContinuous * @return */ public SelectPictureManager setContinuous(boolean isContinuous) { this.isContinuous = isContinuous; return this; } /** * 設定是否需要裁剪 * * @param isNeedCut * @return */ public SelectPictureManager setNeedCrop(boolean isNeedCrop) { this.isNeedCrop = isNeedCrop; return this; } /** * 設定裁剪是否需要縮放 * * @param isScale * @return */ public SelectPictureManager setScaleAble(boolean isScale) { this.isScale = isScale; return this; } /** * 設定裁剪的寬高比例 * * @param x 裁剪比例寬 * @param y 裁剪比例高 * @return */ public SelectPictureManager setAspect(int x, int y) { this.aspectX = x; this.aspectY = y; return this; } /** * 設定裁剪後輸出的尺寸大小 * * @param x 裁剪輸入的寬 * @param y 裁剪輸出的高 * @return */ public SelectPictureManager setOutPutSize(int x, int y) { this.outputX = x; this.outputY = y; return this; } View showView;//使用PopupWindow 顯示時需要一個View,如果覺得不便可以使用dialog /** * 展示選擇拍照的pop框一個是從相機選擇 一個是拍照選擇 * * @param showView 需要展示view */ public void showSelectPicturePopupWindow(View showView) { if (showView == null) { return; } this.showView = showView; if (this.activity == null) { if (this.pictureSelectListner != null) { this.pictureSelectListner.throwError(new NullPointerException("上下文activity不可為空")); } else { throw new NullPointerException("上下文activity不可為空"); } } boolean hasPermission = checkPermission(); if (hasPermission) { //擁有許可權 可以直接開啟 final PopupWindow popupWindow = new PopupWindow(this.activity); LayoutInflater inflater = (LayoutInflater) this.activity .getSystemService(Context.LAYOUT_INFLATER_SERVICE); assert inflater != null; final View mView = inflater.inflate(R.layout.pop_window_view, null); Button btn_camera = (Button) mView.findViewById(R.id.icon_btn_camera); Button btn_select = (Button) mView.findViewById(R.id.icon_btn_choose); Button btn_cancel = (Button) mView.findViewById(R.id.icon_btn_cancel); btn_select.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { choosePhoto(); popupWindow.dismiss(); } }); btn_camera.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { takePhoto(); popupWindow.dismiss(); } }); btn_cancel.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { popupWindow.dismiss(); } }); // 匯入佈局 popupWindow.setContentView(mView); // 設定動畫效果 popupWindow.setAnimationStyle(R.anim.in_top); popupWindow.setWidth(WindowManager.LayoutParams.MATCH_PARENT); popupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT); // 設定可觸 popupWindow.setFocusable(true); ColorDrawable dw = new ColorDrawable(0x0000000); popupWindow.setBackgroundDrawable(dw); // 單擊彈出窗以外處 關閉彈出窗 mView.setOnTouchListener(new View.OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int height = mView.findViewById(R.id.ll_pop).getTop(); int y = (int) event.getY(); if (event.getAction() == MotionEvent.ACTION_UP) { if (y < height) { popupWindow.dismiss(); } } return true; } }); popupWindow.showAtLocation(showView, Gravity.BOTTOM | Gravity.CENTER_HORIZONTAL, 0, 0); } } /** * 先初始化好要儲存的檔案以及對應的uri */ private void initSavedFile(boolean isCrop) { if (!isContinuous && outImageUri != null && !TextUtils.isEmpty(fileName)) {//不是連拍 同時已經存在 return; } File parentFile = new File(Environment.getExternalStorageDirectory(), activity.getPackageName());//先建立包名對應的資料夾 if (!parentFile.exists()) { parentFile.mkdir(); } else if (!parentFile.isDirectory()) { parentFile.delete(); parentFile.mkdir(); } File outputImage = new File(Environment.getExternalStorageDirectory(), activity.getPackageName() + "/" + fileName); try { if (outputImage.exists()) { if (isContinuous || isCrop) {//如果是連拍模式,就需要重新起一個名字 if (isCrop) {//如果是裁剪的話 當這個檔案已存在表示是通過相機拍照過來的,所以需要額外指定裁剪之後儲存的uri oldFileName = fileName; } fileName = System.currentTimeMillis() + ".jpg"; initSavedFile(isCrop);//重新來一下 return; } else { outputImage.delete(); } } outputImage.createNewFile(); } catch (IOException e) { e.printStackTrace(); } //以下uri的處理在targetVersion大於23時,同時在7.0版本以上時需要做區分 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N || isCrop) {//裁剪的時候 儲存需要使用一般的uri 要不然出現無法儲存裁剪的圖片的錯誤 outImageUri = Uri.fromFile(outputImage); } else { //Android 7.0系統開始 使用本地真實的Uri路徑不安全,使用FileProvider封裝共享Uri(相機拍照輸出的uri) outImageUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", outputImage); } } /** * 從相機拍照 */ private void takePhoto() { initSavedFile(false); Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); // 下面這句指定呼叫相機拍照後的照片儲存的路徑(7.0以上需要使用privider獲取的uri) takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, outImageUri); activity.startActivityForResult(takeIntent, TAKE_PHOTO_CODE); } /** * 從相簿選擇 */ private void choosePhoto() { Intent intent = new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); activity.startActivityForResult(intent, CHOOSE_PHOTO_CODE); // 開啟相簿 } /** * 裁剪圖片 */ private void cropPicture(Uri pictureUri) { initSavedFile(true);//從相簿先擇時,是沒有初始化要儲存的檔案路徑以及對應的uri Intent cropIntent = new Intent("com.android.camera.action.CROP"); cropIntent.setDataAndType(pictureUri, "image/*");//7.0以上 輸入的uri需要是provider提供的 // 開啟裁剪:開啟的Intent所顯示的View可裁剪 cropIntent.putExtra("crop", "true"); // 裁剪寬高比 cropIntent.putExtra("aspectX", aspectX); cropIntent.putExtra("aspectY", aspectY); // 裁剪輸出大小 cropIntent.putExtra("outputX", outputX); cropIntent.putExtra("outputY", outputY); cropIntent.putExtra("scale", isScale); /** * return-data * 這個屬性決定onActivityResult 中接收到的是什麼資料型別, * true data將會返回一個bitmap * false,則會將圖片儲存到本地並將我們指定的對應的uri。 */ cropIntent.putExtra("return-data", false); // 當 return-data 為 false 的時候需要設定輸出的uri地址 cropIntent.putExtra(MediaStore.EXTRA_OUTPUT, outImageUri);//輸出的uri為普通的uri,通過provider提供的uri會出現無法儲存的錯誤 // 圖片輸出格式 cropIntent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); cropIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);//不加會出現無法載入此圖片的錯誤 cropIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);// 這兩句是在7.0以上版本當targeVersion大於23時需要 activity.startActivityForResult(cropIntent, PICTURE_CROP_CODE); } /** * 收到圖片結果後的處理邏輯 * * @param requestCode * @param resultCode * @param data */ public void onActivityResult(int requestCode, int resultCode, Intent data) { if (resultCode == Activity.RESULT_OK) { File imageFile = new File(Environment.getExternalStorageDirectory(), activity.getPackageName() + "/" + fileName); Uri inImageUri = null;//需要裁剪時輸入的uri switch (requestCode) { case TAKE_PHOTO_CODE: // TODO: 呼叫相機拍照 if (imageFile == null && !imageFile.exists()) { if (pictureSelectListner != null) { pictureSelectListner.throwError(new NullPointerException("沒有找到對應的路徑")); } return; } if (this.isNeedCrop) { if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { inImageUri = Uri.fromFile(imageFile); } else { //Android 7.0系統開始 使用本地真實的Uri路徑不安全,使用FileProvider封裝共享Uri inImageUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", imageFile); } cropPicture(inImageUri); } else { if (pictureSelectListner != null) { pictureSelectListner.onPictureSelect(imageFile.getAbsolutePath()); } } break; case CHOOSE_PHOTO_CODE: // TODO: 從相簿選擇 Uri uri = data.getData(); String filePath = Util.getFilePathByUri(activity, uri); if (TextUtils.isEmpty(filePath)) { if (pictureSelectListner != null) { pictureSelectListner.throwError(new NullPointerException("沒有找到對應的路徑")); } return; } if (this.isNeedCrop) { File cropFile = new File(filePath); if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { inImageUri = Uri.fromFile(cropFile); } else { //Android 7.0系統開始 使用本地真實的Uri路徑不安全,使用FileProvider封裝共享Uri inImageUri = FileProvider.getUriForFile(activity, activity.getPackageName() + ".fileprovider", cropFile); } cropPicture(inImageUri); } else { if (pictureSelectListner != null) { pictureSelectListner.onPictureSelect(filePath); } } break; case PICTURE_CROP_CODE: // TODO: 圖片裁剪 String cropFile = Util.getFilePathByUri(activity, outImageUri); if (!TextUtils.isEmpty(oldFileName)) {//相機拍照裁剪時刪掉原來的照片 File oldImageFile = new File(Environment.getExternalStorageDirectory(), activity.getPackageName() + "/" + oldFileName); oldImageFile.deleteOnExit(); } if (TextUtils.isEmpty(cropFile)) { if (pictureSelectListner != null) { pictureSelectListner.throwError(new NullPointerException("沒有找到對應的路徑")); } } else { if (pictureSelectListner != null) { pictureSelectListner.onPictureSelect(cropFile); } } break; } } } /** * 6.0版本以上需要檢查許可權 動態的許可權控制 這個地方做了相機、儲存許可權的模糊檢查,雖然單從相簿選擇時不需要相機許可權 */ private boolean checkPermission() { String[] permissions = new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}; //檢查許可權 if (!checkSelfPermission(activity, Manifest.permission.CAMERA) || !checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE) || !checkSelfPermission(activity, Manifest.permission.READ_EXTERNAL_STORAGE) ) { ActivityCompat.requestPermissions(activity, permissions, REQUEST_PERMISSIONS);//沒有許可權時進行許可權申請 return false; } return true; } /** * 根部不同的情況進行許可權檢查,在targetSdkVersion設定為23以下時,不管許可權是否開啟,一般的checkSelfPermission都返回true * * @param activity * @param permission * @return */ private boolean checkSelfPermission(Activity activity, String permission) { int sdkVersion = activity.getApplicationInfo().targetSdkVersion; boolean ret = true; if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (sdkVersion >= Build.VERSION_CODES.M) { ret = activity.checkSelfPermission(permission) == PackageManager.PERMISSION_GRANTED; } else { ret = PermissionChecker.checkSelfPermission(activity, permission) == PermissionChecker.PERMISSION_GRANTED; } } return ret; } /** * 6。0版本以上的許可權申請 * * @param requestCode * @param permissions * @param grantResults */ public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case REQUEST_PERMISSIONS: boolean isGrant = true; for (int index = 0; index < grantResults.length; index++) { if (grantResults[0] != PackageManager.PERMISSION_GRANTED) { isGrant = false; } } if (isGrant) { showSelectPicturePopupWindow(showView); } else { Toast.makeText(activity, "沒有獲取對應的許可權", Toast.LENGTH_SHORT).show(); /** * 跳轉到 APP 詳情的許可權設定頁 */ Intent intent = Util.getSettingIntent(activity); activity.startActivity(intent); } break; default: } } /** * 圖片選定之後的回撥監聽 */ public interface PictureSelectListner { void onPictureSelect(String imagePath);//返回圖片的路徑 void throwError(Exception e);//當錯誤時返回對應的異常 } /** * 工具類 為了保持使用方便,將該類直接放到這裡,如果覺得不便可以單獨出來一個類 */ static class Util { /** * 開啟系統的設定介面 讓使用者自己授權 * * @param context * @return */ public static Intent getSettingIntent(Context context) { Intent localIntent = new Intent(); localIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); if (Build.VERSION.SDK_INT >= 9) { localIntent.setAction("android.settings.APPLICATION_DETAILS_SETTINGS"); localIntent.setData(Uri.fromParts("package", context.getPackageName(), null)); } else if (Build.VERSION.SDK_INT <= 8) { localIntent.setAction(Intent.ACTION_VIEW); localIntent.setClassName("com.android.settings", "com.android.settings.InstalledAppDetails"); localIntent.putExtra("com.android.settings.ApplicationPkgName", context.getPackageName()); } return localIntent; } /** * 獲取uri對應的真是路徑 * * @param context * @param uri * @param selection * @return */ public static String getImagePath(Context context, Uri uri, String selection) { String path = null; // 通過Uri和selection來獲取真實的圖片路徑 Cursor cursor = context.getContentResolver().query(uri, null, selection, null, null); if (cursor != null) { if (cursor.moveToFirst()) { path = cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; } /** * 4。4以上獲取相簿的地址 相簿圖片返回的uri是經過系統封裝過的 * * @param data */ public static String getFilePathByUri(Context context, Uri uri) { String imagePath = null; if (context == null || uri == null) { return imagePath; } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { if (DocumentsContract.isDocumentUri(context, uri)) { // 如果是document型別的Uri,則通過document id處理 String docId = DocumentsContract.getDocumentId(uri); if ("com.android.providers.media.documents".equals(uri.getAuthority())) { String id = docId.split(":")[1]; // 解析出數字格式的id String selection = MediaStore.Images.Media._ID + "=" + id; imagePath = getImagePath(context, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection); } else if ("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(docId)); imagePath = getImagePath(context, contentUri, null); } } else if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是content型別的Uri,則使用普通方式處理 imagePath = getImagePath(context, uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { // 如果是file型別的Uri,直接獲取圖片路徑即可 imagePath = uri.getPath(); } } else { if ("content".equalsIgnoreCase(uri.getScheme())) { // 如果是content型別的Uri,則使用普通方式處理 imagePath = getImagePath(context, uri, null); } else if ("file".equalsIgnoreCase(uri.getScheme())) { // 如果是file型別的Uri,直接獲取圖片路徑即可 imagePath = uri.getPath(); } } return imagePath; } } }
- 佈局檔案(pop_window_view.xml)
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#55000000" android:orientation="vertical"> <LinearLayout android:id="@+id/ll_pop" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:layout_marginLeft="15dp" android:layout_marginRight="15dp" android:orientation="vertical"> <Button android:id="@+id/icon_btn_camera" android:layout_width="match_parent" android:layout_height="42dp" android:background="@drawable/d_arc_5_white" android:text="拍照" android:textColor="@color/blue" /> <View android:layout_width="match_parent" android:layout_height="10px" /> <Button android:id="@+id/icon_btn_choose" android:layout_width="match_parent" android:layout_height="42dp" android:background="@drawable/d_arc_5_white" android:text="從相簿選擇" android:textColor="@color/blue" /> <Button android:id="@+id/icon_btn_cancel" android:layout_width="match_parent" android:layout_height="42dp" android:layout_marginBottom="15dp" android:layout_marginTop="10dp" android:background="@drawable/d_arc_5_white" android:text="取消" android:textColor="@color/black" /> </LinearLayout> </RelativeLayout>
- 按鈕背景(d_arc_5_white.xml)
<?xml version="1.0" encoding="UTF-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="rectangle"> <!-- 填充的顏色 --> <solid android:color="@color/white" /> <!-- 設定按鈕的四個角為弧形 --> <!-- android:radius 弧形的半徑 --> <corners android:radius="5dip" /> </shape>
- 顏色
color/white=“#FFFFFF” color/blue=“#0000FF”
- 7.0以上Provider設定
寫在AndroidManifest.xml中,其中authorities對應的值要和程式碼的中保持一致,否則會丟擲異常
<provider android:name="android.support.v4.content.FileProvider" android:authorities="{packagename}.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/provider_xml" /> </provider>
- provider_xml.xml
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="external-path" path="."/> </paths>
- Provider中paths節點中引數對應說明
子節點 | 對應路徑 |
---|---|
files-path | Context.getFilesDir() |
cache-path | Context.getCacheDir() |
external-path | Environment.getExternalStorageDirectory() |
external-files-path | Context.getExternalFilesDir(null) |
external-cache-path | Context.getExternalCacheDir() |
- 使用方法(fragment中)對應的activity中也需要設定傳遞onActivityResult和onRequestPermissionsResult
SelectPictureManager selectPictureManager; void initSelectPictureManager() { selectPictureManager = new SelectPictureManager(this.getActivity()); selectPictureManager.setPictureSelectListner(new SelectPictureManager.PictureSelectListner() { @Override public void onPictureSelect(String imagePath) { LogUtil.i("選擇圖片的路徑是:" + imagePath); ImageUtil.displayImage(MineFragmentTest.this.getContext(), new File(imagePath), imgv_userhead); } @Override public void throwError(Exception e) { e.printStackTrace(); } }); selectPictureManager.setNeedCrop(true);//需要裁剪 selectPictureManager.setOutPutSize(400, 400);//輸入尺寸 selectPictureManager.setContinuous(true);//設定連拍 selectPictureManager.showSelectPicturePopupWindow(this.getView()); } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); selectPictureManager.onActivityResult(requestCode, resultCode, data); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); selectPictureManager.onRequestPermissionsResult(requestCode, permissions, grantResults); }
- 配置許可權
<uses-feature android:name="android.hardware.camera" /> <!--相機許可權--> <uses-permission android:name="android.permission.CAMERA" /> <!--寫入SD卡的許可權:儲存相機拍照後的照片--> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <!--讀取SD卡的許可權:開啟相簿選取圖片所必須的許可權--> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
參考資料
ofollow,noindex">FileProvider
以上!