Android拍照和從相簿選取圖片,相容7.0
阿新 • • 發佈:2019-02-15
首先配置一下我們的專案
(1)在build.gradle裡新增類庫:
compile 'com.squareup.picasso:picasso:2.5.2'
(2)
2.1、在清單檔案裡面新增許可權
<uses-permission android:name="android.permission.CAMERA"/> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/> <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
2.2、在清單檔案裡面新增provider,紅色部分為專案包名
<provider android:name="android.support.v4.content.FileProvider" android:authorities="com.example.administrator.testb.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS"(3)在res目錄下,新建一個xml資料夾,在xml資料夾裡面,新建一個檔案:file_provider_paths.xmlandroid:resource="@xml/file_provider_paths"/> </provider>
<?xml version="1.0" encoding="utf-8"?> <resources> <paths> <external-path path="" name="myFile"/> </paths> </resources>
(4)引用一個類,封裝了包括拍照,相簿選取圖片,裁剪為一體的類(具體見後面),
這個類是網上找的,原作者徐巨集 http://blog.csdn.net/xh870189248
(5)呼叫這個類,實現拍照和相簿方法
public class PhotoActivity extends Activity { private Button btn; private ImageView iv; private TakePictureManager takePictureManager; private ImageView ivShow; private Button btn_image; //從相簿選取圖片點選事件 private void getImage(){ takePictureManager = new TakePictureManager(this); takePictureManager.setTailor(1, 1, 350, 350); takePictureManager.startTakeWayByAlbum(); takePictureManager.setTakePictureCallBackListener(new TakePictureManager.takePictureCallBackListener() { @Override public void successful(boolean isTailor, File outFile, Uri filePath) { // tvShow.setText(filePath.getPath()); Picasso.with(PhotoActivity.this).load(outFile).error(R.mipmap.ic_launcher).into(ivShow); } @Override public void failed(int errorCode, List<String> deniedPermissions) { } }); } //使用手機拍照點選事件 private void getTake(){ takePictureManager = new TakePictureManager(this); //開啟裁剪 比例 1:3 寬高 350 350 (預設不裁剪) takePictureManager.setTailor(1, 1, 350, 350); //拍照方式 takePictureManager.startTakeWayByCarema(); //回撥 takePictureManager.setTakePictureCallBackListener(new TakePictureManager.takePictureCallBackListener() { //成功拿到圖片,isTailor 是否裁剪? ,outFile 拿到的檔案 ,filePath拿到的URl @Override public void successful(boolean isTailor, File outFile, Uri filePath) { // tvShow.setText(filePath.getPath()); Picasso.with(PhotoActivity.this).load(outFile).error(R.mipmap.ic_launcher).into(ivShow); } //失敗回撥 @Override public void failed(int errorCode, List<String> deniedPermissions) { } }); } //把本地的onActivityResult()方法回撥繫結到物件 @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); takePictureManager.attachToActivityForResult(requestCode, resultCode, data); } //onRequestPermissionsResult()方法許可權回撥繫結到物件 @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); takePictureManager.onRequestPermissionsResult(requestCode, permissions, grantResults); } }
(6)執行效果截圖
封裝類:
package com.example.administrator.testb; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.util.ArrayList; import java.util.List; /* * 專案名:TakePictureManager * 包名:com.xuhong.takepictureandroidn_master * 檔名:TakePictureManager * 建立時間:2017/8/18 12:08 * 建立者: 徐巨集 http://blog.csdn.net/xh870189248 * 描述:只為適配Android N 拍照和本地相簿上傳封裝而生 */ public class TakePictureManager { private Activity mActivity; private Fragment mFragment; //預設不開啟裁剪 private boolean isTailor = false; //裁剪寬高的比例,預設是是 1 :1 private int aspectX = 1; private int aspectY = 1; //裁剪圖片的寬高,預設是是 1 :1 private int outputX = 350; private int outputY = 350; //拿到未裁剪相片的回撥碼(拍照後) private static final int CODE_ORIGINAL_PHOTO_CAMERA = 101; //拿到未裁剪相片的回撥碼(選擇本地相簿後) private static final int CODE_ORIGINAL_PHOTO_ALBUM = 102; //拿到已裁剪相片的回撥碼 private static final int CODE_TAILOR_PHOTO = 103; //布林值,true:在mActivity進行操作 ;false :Fragment操作 private boolean isActicity; //上下文 private Context mContext; //activity private Activity tempActivity; //FileProvider的主機名:一般是包名+".fileprovider" private String FILE_PROVIDER_AUTHORITY; //臨時儲存相片地址 private String imgPath; //最終得到的Url private Uri outputUri; //是否壓縮圖片 預設開啟壓縮圖片的 private boolean isCompressor = true; //圖片回撥介面 private takePictureCallBackListener takeCallBacklistener; //內部許可權介面,學習於郭神 private PermissionListener permissionListener; public TakePictureManager(Activity mActivity) { this.mActivity = mActivity; tempActivity = mActivity; isActicity = true; mContext = mActivity; FILE_PROVIDER_AUTHORITY = mActivity.getPackageName() + ".fileprovider"; } public TakePictureManager(Fragment mFragment) { this.mFragment = mFragment; isActicity = false; mContext = mFragment.getActivity(); tempActivity = mFragment.getActivity(); FILE_PROVIDER_AUTHORITY = mFragment.getActivity().getPackageName() + ".fileprovider"; } //開始拍照 public void startTakeWayByCarema() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //如果是6.0或6.0以上,則要申請執行時許可權,這裡需要申請拍照和寫入SD卡的許可權 requestRuntimePermission(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE} , new PermissionListener() { @Override public void onGranted() { startOpencamera(); } @Override public void onDenied(List<String> deniedPermissions) { if (takeCallBacklistener != null) { takeCallBacklistener.failed(1, deniedPermissions); } } }); return; } startOpencamera(); } //開始從相簿獲取 public void startTakeWayByAlbum() { imgPath = generateImgePath(mContext); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { //如果是6.0或6.0以上,則要申請執行時許可權,這裡需要申請拍照和寫入SD卡的許可權 requestRuntimePermission(new String[]{Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE}, new PermissionListener() { @Override public void onGranted() { startAlbum(); } @Override public void onDenied(List<String> deniedPermissions) { if (takeCallBacklistener != null) { takeCallBacklistener.failed(1, deniedPermissions); } } }); return; } startAlbum(); } private void startAlbum() { Intent intent = new Intent(Intent.ACTION_PICK, null); intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*"); if (isActicity) { mActivity.startActivityForResult(intent, CODE_ORIGINAL_PHOTO_ALBUM); } else { mFragment.startActivityForResult(intent, CODE_ORIGINAL_PHOTO_ALBUM); } } /** * 對外介面,是否裁剪 * * @param aspectX 要裁剪的寬比例 * @param aspectY 要裁剪的高比例 * @param outputX 要裁剪圖片的寬 * @param outputY 要裁剪圖片的高 */ public void setTailor(int aspectX, int aspectY, int outputX, int outputY) { isTailor = true; this.aspectX = aspectX; this.aspectY = aspectY; this.outputX = outputX; this.outputY = outputY; } /** * 裁剪方法 * * @param srcFile * @param output */ private void statZoom(File srcFile, File output) { Intent intent = new Intent("com.android.camera.action.CROP"); intent.setDataAndType(getImageContentUri(mContext, srcFile), "image/*"); // crop為true是設定在開啟的intent中設定顯示的view可以剪裁 intent.putExtra("crop", "true"); intent.putExtra("scale", true);// 去黑邊 // aspectX aspectY 是寬高的比例 intent.putExtra("aspectX", aspectX); intent.putExtra("aspectY", aspectY); // outputX,outputY 是剪裁圖片的寬高 intent.putExtra("outputX", outputX); intent.putExtra("outputY", outputY); intent.putExtra("return-data", false);// true:不返回uri,false:返回uri intent.putExtra("scaleUpIfNeeded", true);//黑邊 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(output)); intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); if (isActicity) { mActivity.startActivityForResult(intent, CODE_TAILOR_PHOTO); } else { mFragment.startActivityForResult(intent, CODE_TAILOR_PHOTO); } } /** * 獲取到的相片回撥方法, * 必須要在當前的Activity或Fragment中的onActivityResult下呼叫! * * @param requestCode * @param resultCode * @param data */ public void attachToActivityForResult(int requestCode, int resultCode, Intent data) { if (resultCode != -1) { return; } Bitmap bm = null; File temFile = null; File srcFile = null; File outPutFile = null; switch (requestCode) { //拍照後得到的圖片 case CODE_ORIGINAL_PHOTO_CAMERA: srcFile = new File(imgPath); outPutFile = new File(generateImgePath(mContext)); outputUri = Uri.fromFile(outPutFile); Uri imageContentUri = getImageContentUri(mContext, srcFile); if (isTailor) { statZoom(srcFile, outPutFile); } else { if (takeCallBacklistener != null) { takeCallBacklistener.successful(false, srcFile, imageContentUri); } } break; //選擇相簿後得到的圖片 case CODE_ORIGINAL_PHOTO_ALBUM: if (data != null) { Uri sourceUri = data.getData(); String pickPath = getPath(mContext, sourceUri); srcFile = new File(pickPath); temFile = srcFile; if (isTailor) { //裁剪之後的檔案和ur outPutFile = new File(generateImgePath(mContext)); outputUri = Uri.fromFile(outPutFile); //發起裁剪請求 statZoom(srcFile, outPutFile); } else { outputUri = Uri.fromFile(srcFile); //如果選擇返回一個壓縮後的圖片 if (isCompressor) { temFile = outputIamge(mContext, compressImage(decodeUriAsBitmap(outputUri), 100)); outputUri = Uri.fromFile(temFile); } if (takeCallBacklistener != null) { takeCallBacklistener.successful(true, temFile, outputUri); } } } else { if (takeCallBacklistener != null) { takeCallBacklistener.failed(0, null); } } break; //裁剪後的圖片: case CODE_TAILOR_PHOTO: //拿到圖片之後,使用者可能會捨棄,所以先判斷 if (data != null) { if (outputUri != null) { //如果是拍照的,刪除臨時檔案 temFile = new File(imgPath); if (temFile.exists()) { temFile.delete(); } //返回一個壓縮後的圖片 if (isCompressor) { temFile = outputIamge(mContext, compressImage(decodeUriAsBitmap(outputUri), 100)); outputUri = Uri.fromFile(temFile); } if (takeCallBacklistener != null) { takeCallBacklistener.successful(true, temFile, outputUri); } } } else { if (takeCallBacklistener != null) { takeCallBacklistener.failed(0, null); } } break; } } //開啟相機 private void startOpencamera() { imgPath = generateImgePath(mContext); File imgFile = new File(imgPath); Uri imgUri = null; //判斷當前手機版本 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { imgUri = FileProvider.getUriForFile(mContext, FILE_PROVIDER_AUTHORITY, imgFile); } else { imgUri = Uri.fromFile(imgFile); } Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri); //判斷當前在哪啟動 if (isActicity) { mActivity.startActivityForResult(intent, CODE_ORIGINAL_PHOTO_CAMERA); } else { mFragment.startActivityForResult(intent, CODE_ORIGINAL_PHOTO_CAMERA); } } //許可權回撥 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { switch (requestCode) { case 1: if (grantResults.length > 0) { List<String> deniedPermissions = new ArrayList<>(); for (int i = 0; i < grantResults.length; i++) { int grantResult = grantResults[i]; String permission = permissions[i]; if (grantResult != PackageManager.PERMISSION_GRANTED) { deniedPermissions.add(permission); } } //被拒絕許可權 if (deniedPermissions.isEmpty()) { permissionListener.onGranted(); } else { permissionListener.onDenied(deniedPermissions); if (takeCallBacklistener != null) { takeCallBacklistener.failed(1, deniedPermissions); } } } break; } } /** * 申請執行時許可權 */ private void requestRuntimePermission(String[] permissions, PermissionListener listener) { permissionListener = listener; List<String> permissionList = new ArrayList<>(); for (String permission : permissions) { if (ContextCompat.checkSelfPermission(mContext, permission) != PackageManager.PERMISSION_GRANTED) { permissionList.add(permission); } } //此處相容了無法在fragment回撥監聽事件 if (!permissionList.isEmpty()) { if (isActicity) { ActivityCompat.requestPermissions((Activity) mContext, permissionList.toArray(new String[permissionList.size()]), 1); } else { mFragment.requestPermissions(permissionList.toArray(new String[permissionList.size()]), 1); } if (takeCallBacklistener != null) { takeCallBacklistener.failed(1, permissionList); } } else { permissionListener.onGranted(); } } public void setTakePictureCallBackListener(takePictureCallBackListener takeCallBacklistener) { this.takeCallBacklistener = takeCallBacklistener; } //得到圖片回撥介面(內部) public interface takePictureCallBackListener { /** * 成功回撥 * * @param isTailor 是否開啟了裁剪 * @param outFile * @param filePath */ void successful(boolean isTailor, File outFile, Uri filePath); /** * 失敗回撥 * * @param errorCode 錯誤碼 0:圖片發生錯誤 1:被拒絕的許可權 * @param deniedPermissions 被拒絕的許可權 */ void failed(int errorCode, List<String> deniedPermissions); } private interface PermissionListener { void onGranted(); void onDenied(List<String> deniedPermissions); } /*---------------------------------------------------------------------------------------------------------------------------------------*/ /*--------------------------------------------以下是檔案操作相關------------------------------------------------------------------------------*/ /*----------------------------------------------------------------------------------------------------------------------------------------*/ private static final String ICON_DIR = "icon"; private static final String APP_STORAGE_ROOT = "AndroidNAdaption"; //判斷SD卡是否掛載 private static boolean isSDCardAvailable() { if (Environment.MEDIA_MOUNTED.equals(Environment .getExternalStorageState())) { return true; } else { return false; } } /** * 獲取app在外接SD卡的路徑 * * @param name * @return */ private static String getAppDir(Context context, String name) { StringBuilder sb = new StringBuilder(); if (isSDCardAvailable()) { sb.append(getAppExternalStoragePath()); } else { sb.append(getCachePath(context)); } sb.append(name); sb.append(File.separator); String path = sb.toString(); if (createDirs(path)) { return path; } else { return null; } } //獲取SD下當前APP的目錄 private static String getAppExternalStoragePath() { StringBuilder sb = new StringBuilder(); sb.append(Environment.getExternalStorageDirectory().getAbsolutePath()); sb.append(File.separator); sb.append(APP_STORAGE_ROOT); sb.append(File.separator); return sb.toString(); } //獲取應用的cache目錄 private static String getCachePath(Context context) { File f = context.getCacheDir(); if (null == f) { return null; } else { return f.getAbsolutePath() + "/"; } } //建立資料夾 private static boolean createDirs(String dirPath) { File file = new File(dirPath); if (!file.exists() || !file.isDirectory()) { return file.mkdirs(); } return true; } //產生圖片的路徑,帶資料夾和檔名,檔名為當前毫秒數 private static String generateImgePath(Context context) { return getAppDir(context, ICON_DIR) + String.valueOf(System.currentTimeMillis()) + ".jpg"; } //裁剪根據檔案路徑獲取uri private 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返回bitmap * * @param uri * @return */ public Bitmap decodeUriAsBitmap(Uri uri) { Bitmap bitmap = null; try { // 先通過getContentResolver方法獲得一個ContentResolver例項, // 呼叫openInputStream(Uri)方法獲得uri關聯的資料流stream // 把上一步獲得的資料流解析成為bitmap bitmap = BitmapFactory.decodeStream(mContext.getContentResolver().openInputStream(uri)); } catch (FileNotFoundException e) { e.printStackTrace(); return null; } return bitmap; } /** * 返回一張壓縮後的圖片 * * @param image 原圖片 * @param size 裁剪之後的大小 * @return */ private static Bitmap compressImage(Bitmap image, int size) { ByteArrayOutputStream baos = new ByteArrayOutputStream(); image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質量壓縮方法,這裡100表示不壓縮,把壓縮後的資料存放到baos中 int options = 100; while (baos.toByteArray().length / 1024 > size) { //迴圈判斷如果壓縮後圖片是否大於100kb,大於繼續壓縮 baos.reset(); options -= 10; image.compress(Bitmap.CompressFormat.JPEG, options, baos);//這裡壓縮options%,把壓縮後的資料存放到baos中 } ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把壓縮後的資料baos存放到ByteArrayInputStream中 Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream資料生成圖片 return bitmap; } //在自定義目錄建立圖片 private static File outputIamge(Context context, Bitmap bitmap) { File outputIamge = new File(generateImgePath(context)); //建立 try { outputIamge.createNewFile(); } catch (IOException e) { e.printStackTrace(); } FileOutputStream fOut = null; try { fOut = <