亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 系統 > Android > 正文

Android實現讀取相機(相冊)圖片并進行剪裁

2020-04-11 11:27:24
字體:
來源:轉載
供稿:網友

我們先說一下思路,在android系統中就自帶了圖片剪切的應用,所以,我們只需要將我們獲取到的相片傳給圖片剪切應用,再將剪切好的相片返回到我們自己的界面顯示就ok了

在開發一些APP的過程中,我們可能涉及到頭像的處理,比如從手機或者相冊獲取頭像,剪裁成自己需要的頭像,設置或上傳頭像等。網上一些相關的資料也是多不勝數,但在實際應用中往往會存在各種問題,沒有一個完美的解決方案。由于近期項目的需求,就研究了一下,目前看來還沒有什么問題。

這里我們只討論獲取、剪裁與設置,上傳流程根據自己的業務需求添加。先上一張流程圖:

這圖是用Google Drive的繪圖工具繪制的,不得不贊嘆Google可以把在線編輯工具做得如此強大。好吧,我就是Google的腦殘粉!回到主題,這是我設計的思路,接下來進行詳細分析:

1、獲得圖片的途徑無非就兩種,第一是相機拍攝,第二是從本地相冊獲取。

2、我在SD卡上創建了一個文件夾,里面有兩個Uri,一個是用于保存拍照時獲得的原始圖片,一個是保存剪裁后的圖片。之前我考慮過用同一個Uri來保存圖片,但是在實踐中遇到一個問題,當拍照后不進行剪裁,那么下次從SD卡拿到就是拍照保存的大圖,不僅丟失了之前剪裁的圖片,還會因為加載大圖導致內存崩潰?;诖丝紤],我選擇了兩個Uri來分別保存圖片。

3、相機拍攝時,我們使用Intent調用系統相機,并將設置輸出設置到SDCard/xx/photo_file.jpg,以下是代碼片段:

//調用系統相機Intent intentCamera = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//將拍照結果保存至photo_file的Uri中,不保留在相冊中intentCamera.putExtra(MediaStore.EXTRA_OUTPUT, imagePhotoUri);startActivityForResult(intentCamera, PHOTO_REQUEST_CAREMA);

在回調時,我們需要對photo_file.jpg調用系統工具進行剪裁,并設置輸出設置到SDCard/xx/crop_file.jpg,以下是代碼片段:

case PHOTO_REQUEST_CAREMA:  if (resultCode == RESULT_OK) {    //從相機拍攝保存的Uri中取出圖片,調用系統剪裁工具    if (imagePhotoUri != null) {      CropUtils.cropImageUri(this, imagePhotoUri, imageUri, ibUserIcon.getWidth(), ibUserIcon.getHeight(), PHOTO_REQUEST_CUT);    } else {      ToastUtils.show(this, "沒有得到拍照圖片");    }  } else if (resultCode == RESULT_CANCELED) {    ToastUtils.show(this, "取消拍照");  } else {    ToastUtils.show(this, "拍照失敗");  }  break;
//調用系統的剪裁處理圖片并保存至imageUri中public static void cropImageUri(Activity activity, Uri orgUri, Uri desUri, int width, int height, int requestCode) {  Intent intent = new Intent("com.android.camera.action.CROP");  intent.setDataAndType(orgUri, "image/*");  intent.putExtra("crop", "true");  intent.putExtra("aspectX", 1);  intent.putExtra("aspectY", 1);  intent.putExtra("outputX", width);  intent.putExtra("outputY", height);  intent.putExtra("scale", true);  //將剪切的圖片保存到目標Uri中  intent.putExtra(MediaStore.EXTRA_OUTPUT, desUri);  intent.putExtra("return-data", false);  intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());  intent.putExtra("noFaceDetection", true);  activity.startActivityForResult(intent, requestCode);}

最后,我們需要在回調中取出crop_file.jpg,因為剪裁時,對圖片已經進行了壓縮,所以也不用擔心內存的問題,在這里我提供兩個方法,第一個是直接獲取原始圖片的Bitmap,第二個是獲取原始圖片并做成圓形,相信大多數的人對后者比較感興趣,哈哈!以下是代碼片段:

case PHOTO_REQUEST_CUT:  if (resultCode == RESULT_OK) {    Bitmap bitmap = decodeUriiAsBimap(this,imageCropUri)  } else if (resultCode == RESULT_CANCELED) {    ToastUtils.show(this, "取消剪切圖片");  } else {    ToastUtils.show(this, "剪切失敗");  }  break;
//從Uri中獲取Bitmap格式的圖片private static Bitmap decodeUriAsBitmap(Context context, Uri uri) {  Bitmap bitmap;  try {    bitmap = BitmapFactory.decodeStream(context.getContentResolver().openInputStream(uri));  } catch (FileNotFoundException e) {    e.printStackTrace();    return null;  }  return bitmap;}
//獲取圓形圖片public static Bitmap getRoundedCornerBitmap(Bitmap bitmap) {  if (bitmap == null) {  return null;  }  Bitmap output = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Bitmap.Config.ARGB_8888);  Canvas canvas = new Canvas(output);  final Paint paint = new Paint();  /* 去鋸齒 */  paint.setAntiAlias(true);  paint.setFilterBitmap(true);  paint.setDither(true);  // 保證是方形,并且從中心畫  int width = bitmap.getWidth();  int height = bitmap.getHeight();  int w;  int deltaX = 0;  int deltaY = 0;  if (width <= height) {    w = width;    deltaY = height - w;  } else {    w = height;    deltaX = width - w;  }  final Rect rect = new Rect(deltaX, deltaY, w, w);  final RectF rectF = new RectF(rect);  paint.setAntiAlias(true);  canvas.drawARGB(0, 0, 0, 0);  // 圓形,所有只用一個  int radius = (int) (Math.sqrt(w * w * 2.0d) / 2);  canvas.drawRoundRect(rectF, radius, radius, paint);  paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));  canvas.drawBitmap(bitmap, rect, rect, paint);  return output;}

4、相冊獲取時,這也是最難的地方。Android 4.4以下的版本,從相冊獲取的圖片Uri能夠完美調用系統剪裁工具,或者直接從選取相冊是帶入剪裁圖片的Intent,而且效果非常完美。但是在Android 4.4及其以上的版本,獲取到的Uri根本無法調用系統剪裁工具,會直接導致程序崩潰。我也是研究了很久,才發現兩者的Uri有很大的區別,Google官方文檔中讓開發者使用Intent.ACTION_GET_CONTENT代替以前的Action,并且就算你仍然使用以前的Action,都會返回一種新型的Uri,我個人猜測是因為Google把所有的內容獲取分享做成一個統一的Uri,如有不對,請指正!想通這一點后,問題就變得簡單了,我把這種新型的Uri重新封裝一次,得到以為"file://..."標準的絕對路勁,傳入系統剪裁工具中,果然成功了,只是這個封裝過程及其艱難,查閱了很多資料,終于還是拿到了。下面說下具體步驟:

第一、調用系統相冊,以下是代碼片段:

//調用系統相冊  Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT);  photoPickerIntent.setType("image/*");  startActivityForResult(photoPickerIntent, PHOTO_REQUEST_GALLERY);

第二、在回調中,重新封裝Uri,并調用系統剪裁工具將輸出設置到crop_file.jpg,調用系統剪裁工具代碼在拍照獲取的步驟中已經貼出,這里就不重復制造車輪了,重點貼重新封裝Uri的代碼,以下是代碼片段:

case PHOTO_REQUEST_GALLERY:  if (resultCode == RESULT_OK) {    //從相冊選取成功后,需要從Uri中拿出圖片的絕對路徑,再調用剪切    Uri newUri = Uri.parse("file:///" + CropUtils.getPath(this, data.getData()));    if (newUri != null) {      CropUtils.cropImageUri(this, newUri, imageUri, ibUserIcon.getWidth(),      ibUserIcon.getHeight(), PHOTO_REQUEST_CUT);    } else {      ToastUtils.show(this, "沒有得到相冊圖片");    }  } else if (resultCode == RESULT_CANCELED) {    ToastUtils.show(this, "從相冊選取取消");  } else {    ToastUtils.show(this, "從相冊選取失敗");  }  break;
@SuppressLint("NewApi")public static String getPath(final Context context, final Uri uri) {final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;// DocumentProviderif (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {  // ExternalStorageProvider  if (isExternalStorageDocument(uri)) {    final String docId = DocumentsContract.getDocumentId(uri);    final String[] split = docId.split(":");    final String type = split[0];    if ("primary".equalsIgnoreCase(type)) {      return Environment.getExternalStorageDirectory() + "/"+ split[1];    }  }  // DownloadsProvider  else if (isDownloadsDocument(uri)) {    final String id = DocumentsContract.getDocumentId(uri);    final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(id));    return getDataColumn(context, contentUri, null, null);  }  // MediaProvider  else if (isMediaDocument(uri)) {    final String docId = DocumentsContract.getDocumentId(uri);    final String[] split = docId.split(":");    final String type = split[0];    Uri contentUri = null;    if ("image".equals(type)) {      contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;    } else if ("video".equals(type)) {      contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;    } else if ("audio".equals(type)) {      contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;    }    final String selection = "_id=?";    final String[] selectionArgs = new String[]{split[1]};    return getDataColumn(context, contentUri, selection,selectionArgs);    }  }  // MediaStore (and general)  else if ("content".equalsIgnoreCase(uri.getScheme())) {    return getDataColumn(context, uri, null, null);  }  // File  else if ("file".equalsIgnoreCase(uri.getScheme())) {    return uri.getPath();  }  return null;}/*** Get the value of the data column for this Uri. This is useful for* MediaStore Uris, and other file-based ContentProviders.** @param context    The context.* @param uri      The Uri to query.* @param selection   (Optional) Filter used in the query.* @param selectionArgs (Optional) Selection arguments used in the query.* @return The value of the _data column, which is typically a file path.*/private static String getDataColumn(Context context, Uri uri,String selection, String[] selectionArgs) {  Cursor cursor = null;  final String column = "_data";  final String[] projection = {column};  try {    cursor = context.getContentResolver().query(uri, projection,selection, selectionArgs, null);    if (cursor != null && cursor.moveToFirst()) {      final int column_index = cursor.getColumnIndexOrThrow(column);      return cursor.getString(column_index);    }  } finally {    if (cursor != null)      cursor.close();  }  return null;}/*** @param uri The Uri to check.* @return Whether the Uri authority is ExternalStorageProvider.*/private static boolean isExternalStorageDocument(Uri uri) {  return "com.android.externalstorage.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is DownloadsProvider.*/private static boolean isDownloadsDocument(Uri uri) {  return "com.android.providers.downloads.documents".equals(uri.getAuthority());}/*** @param uri The Uri to check.* @return Whether the Uri authority is MediaProvider.*/private static boolean isMediaDocument(Uri uri) {  return "com.android.providers.media.documents".equals(uri.getAuthority());}

后續的系統剪裁工具調用跟拍照獲取步驟一致,請參見上的代碼。

5、所有步驟完成,在Nexus 5設備中的最新系統中測試通過,在小米、三星等一些設備中表現也很完美。如果在你的設備上存在缺陷,一定要跟帖給我反饋,謝謝!

文章結尾附上一個網友的完整示例,給了我很多的參考

package com.only.android.app; import java.io.File; import android.app.Activity;import android.app.AlertDialog;import android.content.DialogInterface;import android.content.Intent;import android.graphics.Bitmap;import android.graphics.BitmapFactory;import android.net.Uri;import android.os.Bundle;import android.os.SystemClock;import android.provider.MediaStore;import android.view.View;import android.widget.Button;import android.widget.ImageView; import com.only.android.R; public class CopyOfImageScaleActivity extends Activity implements View.OnClickListener {  /** Called when the activity is first created. */  private Button selectImageBtn;  private ImageView imageView;     private File sdcardTempFile;  private AlertDialog dialog;  private int crop = 180;   @Override  public void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.imagescale);     selectImageBtn = (Button) findViewById(R.id.selectImageBtn);    imageView = (ImageView) findViewById(R.id.imageView);     selectImageBtn.setOnClickListener(this);    sdcardTempFile = new File("/mnt/sdcard/", "tmp_pic_" + SystemClock.currentThreadTimeMillis() + ".jpg");   }   @Override  public void onClick(View v) {    if (v == selectImageBtn) {      if (dialog == null) {        dialog = new AlertDialog.Builder(this).setItems(new String[] { "相機", "相冊" }, new DialogInterface.OnClickListener() {          @Override          public void onClick(DialogInterface dialog, int which) {            if (which == 0) {              Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");              intent.putExtra("output", Uri.fromFile(sdcardTempFile));              intent.putExtra("crop", "true");              intent.putExtra("aspectX", 1);// 裁剪框比例              intent.putExtra("aspectY", 1);              intent.putExtra("outputX", crop);// 輸出圖片大小              intent.putExtra("outputY", crop);              startActivityForResult(intent, 101);            } else {              Intent intent = new Intent("android.intent.action.PICK");              intent.setDataAndType(MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");              intent.putExtra("output", Uri.fromFile(sdcardTempFile));              intent.putExtra("crop", "true");              intent.putExtra("aspectX", 1);// 裁剪框比例              intent.putExtra("aspectY", 1);              intent.putExtra("outputX", crop);// 輸出圖片大小              intent.putExtra("outputY", crop);              startActivityForResult(intent, 100);            }          }        }).create();      }      if (!dialog.isShowing()) {        dialog.show();      }    }  }   @Override  protected void onActivityResult(int requestCode, int resultCode, Intent intent) {    if (resultCode == RESULT_OK) {      Bitmap bmp = BitmapFactory.decodeFile(sdcardTempFile.getAbsolutePath());      imageView.setImageBitmap(bmp);    }  }}

最后再亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

国产不卡在线观看| 欧美一区二区三区……| 久久久久久久久久婷婷| 亚洲综合自拍一区| 欧美黄色片视频| 欧美日韩综合视频网址| 亚洲片在线观看| 欧美激情精品久久久久久久变态| 欧美老少配视频| 精品亚洲一区二区三区在线观看| 欧美在线视频免费观看| 精品电影在线观看| 精品亚洲va在线va天堂资源站| 成人免费观看49www在线观看| 在线观看不卡av| 国产精品久久久久秋霞鲁丝| 亚洲奶大毛多的老太婆| 久久久亚洲网站| 91精品综合久久久久久五月天| 国产亚洲人成a一在线v站| 成人免费看吃奶视频网站| 黑人精品xxx一区一二区| 亚洲精品白浆高清久久久久久| 久久精品国产精品亚洲| 欧美精品videosex牲欧美| 性欧美暴力猛交69hd| 久久久久日韩精品久久久男男| 亚洲wwwav| 日本sm极度另类视频| 色综合视频网站| 国外成人在线直播| 中文字幕免费国产精品| 国产v综合ⅴ日韩v欧美大片| 国产精品9999| 国产一区二区丝袜高跟鞋图片| 伊人久久大香线蕉av一区二区| 少妇高潮 亚洲精品| 91九色国产视频| 欧美激情在线有限公司| 亚洲成人黄色在线| 最近中文字幕mv在线一区二区三区四区| 超碰日本道色综合久久综合| 精品无码久久久久久国产| 国产精品无av码在线观看| 国产美女91呻吟求| 欧美午夜精品久久久久久久| 欧美亚洲成人网| 中文字幕亚洲图片| 亚洲美女视频网站| 亚洲欧美日韩视频一区| 欧美华人在线视频| 久久人体大胆视频| 国产视频亚洲视频| 激情懂色av一区av二区av| 久久影院模特热| 992tv成人免费视频| 在线亚洲男人天堂| 国产精品流白浆视频| 欧美大成色www永久网站婷| 精品一区精品二区| 日韩中文理论片| 日韩久久午夜影院| 久热精品视频在线| 国产成+人+综合+亚洲欧洲| 色综合久久88色综合天天看泰| 欧美性jizz18性欧美| 国产美女扒开尿口久久久| 亚洲国产日韩欧美综合久久| 日韩av在线网站| 91wwwcom在线观看| 日韩精品在线免费观看视频| 国产精品自产拍在线观看| 亚洲成人网av| 国产精品欧美日韩一区二区| 亚洲男人天堂网| 精品呦交小u女在线| 爽爽爽爽爽爽爽成人免费观看| 操91在线视频| 在线看日韩欧美| 日韩高清免费观看| 欧美一级成年大片在线观看| 91精品国产高清自在线看超| 亚洲女人天堂成人av在线| 中文字幕在线国产精品| 亚洲欧美精品中文字幕在线| 韩国国内大量揄拍精品视频| 日韩欧美一区二区在线| www日韩中文字幕在线看| 日韩中文字幕不卡视频| 久久国产精品久久精品| 欧美电影免费观看| 国产精品自产拍高潮在线观看| 韩剧1988免费观看全集| 国产精品揄拍一区二区| 亚洲一区亚洲二区亚洲三区| 色妞久久福利网| 91av在线看| 亚洲第一中文字幕在线观看| 亚洲国产天堂久久国产91| 国产精品日韩欧美综合| 久久久久久久国产精品视频| 欧美成人激情视频| 中文日韩电影网站| 92福利视频午夜1000合集在线观看| 亚洲精品久久久一区二区三区| 国产精品精品视频一区二区三区| 国产精品久久久91| 日本精品性网站在线观看| 国产精品亚洲视频在线观看| 欧美黄色片在线观看| 亚洲国产精品大全| 综合av色偷偷网| 91日本在线视频| 中文字幕亚洲一区二区三区| 成人h片在线播放免费网站| 麻豆乱码国产一区二区三区| 国产mv免费观看入口亚洲| 成人国产精品一区二区| 在线播放日韩专区| 亚洲视频综合网| 色偷偷9999www| 亚洲小视频在线| 日韩视频免费大全中文字幕| 亚洲综合中文字幕在线观看| 久久夜色精品国产欧美乱| 在线精品高清中文字幕| 亚洲第一免费播放区| 97香蕉超级碰碰久久免费软件| 国产精品色悠悠| 亚洲欧洲中文天堂| 5252色成人免费视频| 成人免费网视频| 一道本无吗dⅴd在线播放一区| 亚洲国产成人爱av在线播放| 日韩免费av片在线观看| 久久成人精品电影| 欧美成人免费大片| 亚洲欧美综合另类中字| 亚洲精品久久久一区二区三区| 欧美日韩成人在线视频| 国产精品一区二区三区久久久| 国产精品久久二区| 成人精品视频在线| 久久久免费电影| 国产精品盗摄久久久| 自拍亚洲一区欧美另类| 国产精品女人久久久久久| 国产97在线观看| 欧美超级免费视 在线| 日本欧美一二三区| 精品欧美aⅴ在线网站| 国产精品女人久久久久久| 久久精品免费电影| 91亚洲午夜在线| 国产精品久久久久久久久久久新郎| 成人乱人伦精品视频在线观看| 91精品国产91久久久久久| 亚洲另类激情图| 中文字幕一精品亚洲无线一区| 国产在线久久久| 国产精品第一视频| 国产精品视频男人的天堂| 亚洲人成在线一二| www.午夜精品|