Android图片裁剪(拍照和从相册选择)

一、获取图片的两种方式

1.获取图片的两种方式的简单使用
Android获取图片有两种途径,拍照和从相册选取,我们都只到这两种的简单用法:
拍照
   
   
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, cameraUri);
从相册选取
   
   
Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
intent.setType("image/*");
startActivityForResult(intent, PHOTO_RESULT);
然后直接在onActivityResult的方法中,获取我们所需要的图片既可以。在onActivityResult方法中获取图片有两种方式,根据data.getParcelableExtra("data");获取的Parcelable结果强转为Bitmap;另外一种方式是根据我们intent设置的输出结果保存的Uri获取图片:
    
    
MediaStore.EXTRA_OUTPUT:保存相机拍完照片后保存的照片的路径Uri

2.通过onActivityResult方法中data.getParcelableExtra("data")获取图片的详解
如果是打开相机通过这种方式获取图片,会存在一定的问题。获取的图片的Bitmap不能够太大,具体解析如下:

在Android中,Intent触发Camera程序,拍好照片后,将会返回数据,但是考虑到内存问题,Camera不会将全尺寸的图像返回给调用的Activity,一般情况下,有可能返回的是缩略图,比如120*160px。

    这是为什么呢?这不是一个Bug,而是经过精心设计的,却对开发者不透明。

    以我的小米手机为例,摄像头800W像素,根据我目前设置拍出来的图片尺 寸为3200*2400px。有人说,那就返回呗,大不了耗1-2M的内存,不错,这个尺寸的图片确实只有1.8M左右的大小。但是你想不到的是,这个尺 寸对应的Bitmap会耗光你应用程序的所有内存。Android出于安全性考虑,只会给你一个寒碜的缩略图。

    在Android2.3中,默认的Bitmap为32位,类型是ARGB_8888,也就意味着一个像素点占用4个字节的内存。我们来做一个简单的计算题:3200*2400*4 bytes =   30M。

    如此惊人的数字!哪怕你愿意为一张生命周期超不过10s的位图愿意耗费这么巨大的内存,Android也不会答应的。

?
1
2
Mobile devices typically have constrained system resources.
Android devices can have as little as 16MB of memory available to a single application.

    这是Android Doc的原文,虽然不同手机系统的厂商可能围绕16M这个数字有微微的上调,但是这30M,一般的手机还真挥霍不起。也只有小米这种牛机,内存堪比个人PC,本着土财主般挥金如土的霸气才能做到。


二、图片裁剪
1.图片裁剪简介
  通过上面的知识点了解以后,我们知道在什么情况下使用data.getParcelableExtra("data");获取Bitmap的对象(如果要求的图片较小可以考虑),如果图片过大则需要使用Uri的方式获取图片。
    Android中有自带的图片裁剪功能,用法也比较简单,如下:
   
   
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", 200);
intent.putExtra("outputY", 200);
intent.putExtra("return-data", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cropFile));
startActivityForResult(intent, responseCode);
具体选项的含义如下:
附加选项 数据类型  描述
crop String 发送裁剪信号
aspectX int X方向上的比例
aspectY int Y方向上的比例
outputX int 裁剪区的宽
outputY int 裁剪区的高
scale boolean 是否保留比例
return-data boolean 是否将数据保留在Bitmap中返回
data Parcelable 相应的Bitmap数据
circleCrop String 圆形裁剪区域?
MediaStore.EXTRA_OUTPUT ("output")  URI 将URI指向相应的file:///...,详见代码示例 
MediaStore.EXTRA_OUTPUT 和return-data两个比较难以理解,意思就是裁剪以后的图片需要保存的路径。
return-data设置为true和false的情况不同,具体如下:
两种方式从这个Intent中取得返回的bitmap:获取内部数据或者提供一个Uri以便程序可以将数据写入。 
  方法1: 如 果你将return-data设置为“true”,你将会获得一个与内部数据关联的Action,并且bitmap以此方式返回: (Bitmap)extras.getParcelable("data")。注意:如果你最终要获取的图片非常大,那么此方法会给你带来麻烦,所以你要 控制outputX和outputY保持在较小的尺寸。鉴于此原因,在我的代码中没有使用此方法 ((Bitmap)extras.getParcelable("data"))。 
下面是CropImage.java的源码片段:
    
    
// Return the cropped image directly or save it to the specified URI.
Bundle myExtras = getIntent().getExtras();
if (myExtras != null && (myExtras.getParcelable("data") != null || myExtras.getBoolean("return-data")))
{
Bundle extras = new Bundle();
extras.putParcelable("data", croppedImage);
setResult(RESULT_OK,(new Intent()).setAction("inline-data").putExtras(extras));
finish();
}
方法2:如果你将return-data设置为“false”,那么在onActivityResult的Intent数据中你将不会接收到任何Bitmap,相反,你需要将MediaStore.EXTRA_OUTPUT关联到一个Uri,此Uri是用来存放Bitmap的。 

但是还有一些条件,首先你需要有一个短暂的与此Uri相关联的文件地址,当然这不是个大问题(除非是那些没有sdcard的设备)。

下面是CropImage.java关于操作Uri的源码片段:

     
     
if (mSaveUri != null) {
OutputStream outputStream = null;
try {
outputStream = mContentResolver.openOutputStream(mSaveUri);
if (outputStream != null) {
croppedImage.compress(mOutputFormat, 75, outputStream);
}
} catch (IOException ex) {
// TODO: report error to caller
Log.e(TAG, "Cannot open file: " + mSaveUri, ex);
} finally {
Util.closeSilently(outputStream);
}
Bundle extras = new Bundle();
setResult(RESULT_OK, new Intent(mSaveUri.toString()).putExtras(extras));
}

2.图片裁剪的相关代码
(1)打开相机拍照后进行图片裁剪的相关代码:
      
      
public void startPhotoZoom(Uri uri, int responseCode) {
 
int rannum = (int) (new Random().nextDouble() * (99999 - 10000 + 1)) + 10000;
cropPath = Environment.getExternalStorageDirectory().getPath() + "/elife/.temp/"
+ (System.currentTimeMillis() + rannum + ".jpg");
cropFile = new File(cropPath);
 
if (!cropFile.exists()) {
File vDirPath = cropFile.getParentFile();
vDirPath.mkdirs();
} else {
if (cropFile.exists()) {
cropFile.delete();
}
}
 
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", 200);
intent.putExtra("outputY", 200);
intent.putExtra("return-data", true);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cropFile));
startActivityForResult(intent, responseCode);
}

(2)从相册选取图片后进行裁剪(以下方式解决了android6.0和魅族手机无法打开裁剪功能的bug问题)
在Android4.4系统中,通过 ACTION_GET_CONTENT的方法,返回的uri跟4.3是完全 不一样的,4.3返回的是带文件路径的,而4.4返回的却是 content://com.android.providers.media.documents/document/image:3951这样的,没有路径,所以我们需要将content替换成file,详见代码:

      
      
package com.icbc.elife.utils;
 
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
 
@SuppressLint("NewApi")
public class CropUtils {
//调用系统的剪裁处理图片并保存至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);
}
@SuppressLint("NewApi")
@TargetApi(Build.VERSION_CODES.KITKAT)
public static String getPath(final Context context, final Uri uri) {
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
 
// DocumentProvider
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
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];
}
} 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);
} 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);
}
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
return getDataColumn(context, uri, null, null);
} 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());
}
 
}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值