在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,所以用拍照得到的图片需要裁剪处理后使用。

Android裁剪图片的Intent附加数据的具体含义,拿图说事儿:

Intent("com.android.camera.action.CROP")对应的所有可选数据都一目了然。在了解上面个个选项的含义之后,我们将目光着眼于三个极为重要的选项:

data、MediaStore.EXTRA_OUTPUT以及return-data。

data和MediaStore.EXTRA_OUTPUT都是可选的传入数据选项,你可以选择设置data为Bitmap,或者将相应的数据与URI关联起来,你也可以选择是否返回数据(return-data: true)。

为什么还有不用返回数据的选项?如果对URI足够了解的话,应该知道URI与File相似,你所有的操作如裁剪将数据都保存在了URI中,你已经持有了相应的URI,也就无需多此一举,再返回Bitmap了。

前面已经说到,可以设置data为Bitmap,但是这种操作的限制在于,你的Bitmap不能太大。因此,我们前进的思路似乎明确了:截大图用URI,小图用Bitmap。

我将这个思路整理成一张图片:

从相册截图

在上面,我就拍照截图这一需求进行了详细的分析,试图让大家了解Android本身的限制,以及我们应当采取的实现方案。

根据我们的分析与总结,图片的来源有拍照和相册,而可采取的操作有

  • 使用Bitmap并返回数据
  • 使用Uri不返回数据

前面我们了解到,使用Bitmap有可能会导致图片过大,而不能返回实际大小的图片,我将采用大图Uri,小图Bitmap的数据存储方式。

我们将要使用到URI来保存拍照后的图片:

private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";//temp file
Uri imageUri = Uri.parse(IMAGE_FILE_LOCATION);//The Uri to store the big bitmap

不难知道,我们从相册选取图片的Action为Intent.ACTION_GET_CONTENT。

根据我们上一篇博客的分析,我准备好了两个实例的Intent。

一、从相册截大图:

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);

intent.setType("image/*");

intent.putExtra("crop", "true");

intent.putExtra("aspectX", 2);

intent.putExtra("aspectY", 1);

intent.putExtra("outputX", 600);

intent.putExtra("outputY", 300);

intent.putExtra("scale", true);

intent.putExtra("return-data", false);

intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());

intent.putExtra("noFaceDetection", true); // no face detection

startActivityForResult(intent, CHOOSE_BIG_PICTURE);

二、从相册截小图

Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);

intent.setType("image/*");

intent.putExtra("crop", "true");

intent.putExtra("aspectX", 2);

intent.putExtra("aspectY", 1);

intent.putExtra("outputX", 200);

intent.putExtra("outputY", 100);

intent.putExtra("scale", true);

intent.putExtra("return-data", true);

intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());

intent.putExtra("noFaceDetection", true); // no face detection

startActivityForResult(intent, CHOOSE_SMALL_PICTURE);

三、对应的onActivityResult可以这样处理返回的数据

switch (requestCode) {

    case CHOOSE_BIG_PICTURE:

    Log.d(TAG, "CHOOSE_BIG_PICTURE: data = " + data);//it seems to be null

    if(imageUri != null){

    Bitmap bitmap = decodeUriAsBitmap(imageUri);//decode bitmap

    imageView.setImageBitmap(bitmap);

    }

    break;

    case CHOOSE_SMALL_PICTURE:

    if(data != null){

    Bitmap bitmap = data.getParcelableExtra("data");

    imageView.setImageBitmap(bitmap);

        }else{

    Log.e(TAG, "CHOOSE_SMALL_PICTURE: data = " + data);

        }

    break;

    default:

    break;

    }

private Bitmap decodeUriAsBitmap(Uri uri){

    Bitmap bitmap = null;

    try {

    bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(uri));

        } catch (FileNotFoundException e) {

    e.printStackTrace();

        return null;

        }

    return bitmap;

    }

拍照截图

拍照截图有点儿特殊,要知道,现在的Android智能手机的摄像头都是几百万的像素,拍出来的图片都是非常大的。因此,我们不能像对待相册截图一样使用Bitmap小图,无论大图小图都统一使用Uri进行操作。

一、首先准备好需要使用到的Uri:

private static final String IMAGE_FILE_LOCATION = "file:///sdcard/temp.jpg";//temp file
Uri imageUri = Uri.parse(IMAGE_FILE_LOCATION);//The Uri to store the big bitmap

二、使用MediaStore.ACTION_IMAGE_CAPTURE可以轻松调用Camera程序进行拍照:

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);//action is capture

intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

startActivityForResult(intent, TAKE_BIG_PICTURE);//or TAKE_SMALL_PICTURE

三、接下来就可以在 onActivityResult中拿到返回的数据(Uri),并将Uri传递给截图的程序。

switch (requestCode) {

    case TAKE_BIG_PICTURE:

    Log.d(TAG, "TAKE_BIG_PICTURE: data = " + data);//it seems to be null
//TODO sent to crop cropImageUri(imageUri, 800, 400, CROP_BIG_PICTURE); break; case TAKE_SMALL_PICTURE: Log.i(TAG, "TAKE_SMALL_PICTURE: data = " + data); //TODO sent to crop cropImageUri(imageUri, 300, 150, CROP_SMALL_PICTURE); break; default: break; }

可以看到,无论是拍大图片还是小图片,都是使用的Uri,只是尺寸不同而已。我们将这个操作封装在一个方法里面。

private void cropImageUri(Uri uri, int outputX, int outputY, int requestCode){

    Intent intent = new Intent("com.android.camera.action.CROP");

    intent.setDataAndType(uri, "image/*");

    intent.putExtra("crop", "true");

    intent.putExtra("aspectX", 2);

    intent.putExtra("aspectY", 1);

    intent.putExtra("outputX", outputX);

    intent.putExtra("outputY", outputY);

    intent.putExtra("scale", true);

    intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

    intent.putExtra("return-data", false);

    intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());

    intent.putExtra("noFaceDetection", true); // no face detection

    startActivityForResult(intent, requestCode);

    }

四、最后一步,我们已经将数据传入裁剪图片程序,接下来要做的就是处理返回的数据了:

    switch (requestCode) {

    case CROP_BIG_PICTURE://from crop_big_picture

    Log.d(TAG, "CROP_BIG_PICTURE: data = " + data);//it seems to be null

    if(imageUri != null){

    Bitmap bitmap = decodeUriAsBitmap(imageUri);

    imageView.setImageBitmap(bitmap);

    }

    break;

    case CROP_SMALL_PICTURE:

    if(imageUri != null){

    Bitmap bitmap = decodeUriAsBitmap(imageUri);

    imageView.setImageBitmap(bitmap);

    }else{

    Log.e(TAG, "CROP_SMALL_PICTURE: data = " + data);

    }

    break;

    default:

    break;

    }
 
摘选自:https://github.com/ryanhoo/PhotoCropper
 

Android 拍照或从相册取图片并裁剪的更多相关文章

  1. Android 拍照、从相册获取及裁剪的相关实现

    首先这些功能都是通过Intent去启动系统的服务去实现的,所以自然就有相应的Action.相关Actiong如下: 拍照——MediaStore.ACTION_IMAGE_CAPTURE (" ...

  2. android 拍照和从相册选择组件

    android 拍照及从相册选择组件 单独封装到一个 activity 中便于更好的复用 拍照或从相册选择成功后使用 EventBus 发出广播回传图片路径,和调用者充分解耦合 根据传入参数支持裁剪和 ...

  3. Android 拍照或者从相册获取图片的实现

    我们常常会用到上传头像,或者发帖子的时候选择本地图片上传的功能.这个很常见 今天因为app的需求我研究了下.现在分享下. 其实不论是通过拍照还是从相册选取都会用到Intent 这是系统提供给我们用来调 ...

  4. android --拍照,从相册获取图片,兼容高版本,兼容小米手机

    前几天做项目中选择图片的过程中遇到高版本和小米手机出现无法选择和崩溃的问题,现在记录下来,后面出现同类问题,也好查找 1,定义常量: private static final int TAKE_PIC ...

  5. Android拍照和从相册获取照片

    1.从相册获取照片 private void openAlumb() { //mRxPermissions:三方权限库 mRxPermissions .request(Manifest.permiss ...

  6. Android拍照后更新相册

    方法一: Uri updateUri = Uri.fromFile(file); Intent updateIntent = new Intent(Intent.ACTION_MEDIA_SCANNE ...

  7. IOS UIImagePickerController(拍照或者读取相册)

      UIImagePickerController ● 使用UIImagePickerController就可以进行拍照或者读取相册 ● 通过sourceType属性来决定拍照还是读取相册 ➢ UII ...

  8. android——拍照,相册图片剪切其实就这么简单

    接触android这么久了.还没有真正的浩浩看看android拍照,相册图片剪切到底是怎么回事,每次都是从别人的代码一扣,就过来了.其实,谷歌提供的API已经很强大.只需要用的好,就那么几句就可以搞定 ...

  9. Android生成二维码--拍照或从相册选取图片

    拍照或从相册选择图片是我们日常开发中经常使用到的,可以说是必须掌握的东西.上一篇我介绍了如何生成自定义二维码<Android生成自定义二维码>,其中logo和代替黑色色块的图片都是写死的, ...

随机推荐

  1. springmvc map

    /** * 目标方法可以添加 Map 类型(实际上也可以是 Model 类型或 ModelMap 类型)的参数. * @param map * @return */ @RequestMapping(& ...

  2. angularjs中directive声明scope对象的用法

    总的来说用法 分三种: >1: scope: false  --> 继承父域,实现 双向数据绑定 示例代码 可自测: <!DOCTYPE html> <html lang ...

  3. Spring 之混合配置

    [JavaConfig 导入另外一个 JavaConfig & JavaConfig 导入 XML] package soundsystem.config; import org.spring ...

  4. CODE FESTIVAL 2015 決勝(部分)

    CODE FESTIVAL 2015 決勝(部分) B - ダイスゲーム 题意: 给\(N\)个\(6\)个面骰子,然后问掷到概率最大的点数是多少. 分析: 随便打表随便发现是\(\huge\left ...

  5. spark学习12(Wordcount程序之spark-shell)

    在目录/home/hadoop/2016113012下有文件words.txt hello scala hello java hello python hello wujiadong 上传该文件到hd ...

  6. UVA 10900 So you want to be a 2n-aire? (概率dp)

    题意:玩家初始的金额为1:给出n,表示有n道题目:t表示说答对一道题目的概率在t到1之间均匀分布. 每次面对一道题,可以选择结束游戏,获得当前奖金:或者回答下一道问题,答对的话奖金翻倍,答错的话结束游 ...

  7. POJ 3977 - subset - 折半枚举

    2017-08-01 21:45:19 writer:pprp 题目: • POJ 3977• 给定n个数,求一个子集(非空)• 使得子集内元素和的绝对值最小• n ≤ 35 AC代码如下:(难点:枚 ...

  8. 使用js写的确认提交表单确认框

    js脚本 <script language="Javascript"> function validator() { if(confirm("确认要执行此操作 ...

  9. log4j.appender.AFile.File日志的相对路径

    log4j.appender.AFile=org.apache.log4j.DailyRollingFileAppenderlog4j.appender.AFile.DatePattern='.'yy ...

  10. scala学习手记23 - 函数值

    scala的一个最主要的特性就是支持函数编程.函数是函数编程中的一等公民:函数可以作为参数传递给其他函数,可以作为其他函数的返回值,甚至可以在其它函数中嵌套.这些高阶函数称为函数值. 举一个简单的例子 ...