[Android]图库/拍照获取图片后裁剪处理(兼容4.4+)

需求:

  • 从图库或调用系统相机拍照获取图片
  • 复制图片至新目录
  • 使用新目录下的图片裁剪

遇到的问题:

  • Android 6.0+ 无法使用
  • Android 4.4 调用裁剪时奔溃
  • 红米 note1 获取原图片后无法显示 X

调用系统图库

先贴代码

 boolean isKitKatO = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
                Intent getAlbum;
                if (isKitKatO) {
                    getAlbum = new Intent(Intent.ACTION_OPEN_DOCUMENT);
                } else {
                    getAlbum = new Intent(Intent.ACTION_GET_CONTENT);
                }
                getAlbum.setType("image/*");
                startActivityForResult(getAlbum, FILE_REQUEST_CODE_PICTURE);

在Android 4.4之前,调用系统图库拿到图片的Uri和4.4或以上的Uri是有很大区别,单单使用Intent.ACTION_GET_CONTENT 在4.4或以上的版本是无法解析拿到的Uri的。因此,在调用图库之前,版本是Build.VERSION_CODES.KITKAT 或以上的版本,官方建议用ACTION_OPEN_DOCUMENT。这就是我上面列举的第二个问题的解决方式。
解决方案参照了 东山少爷猪头的这篇blog

重写onActivityResult方法接收返回状态和数据

if (FILE_REQUEST_CODE_PICTURE == requestCode && data != null && resultCode == RESULT_OK) {
            //新的图片路径
            CropPicPath = MyApplication.BasePath + System.currentTimeMillis() + ".jpg";
            Uri selectImageUri = data.getData();
            //拿到相册图片后进入裁剪
            if (selectImageUri != null) {
                //将选取的图片uri转化为path
                selectImagePath = uri2path(selectImageUri);
                //复制一份到新的路径
                FileUtil.copyFile(selectImagePath, CropPicPath);
                //将截取的图片路径转化为uri
                tempUri = Uri.fromFile(new File(CropPicPath));
                //带入截图
                cropPicUtil.startBigPhotoCrop(tempUri, 240, 240);
            } else {
                Toast.makeText(getApplicationContext(), "没有选择文件", Toast.LENGTH_SHORT).show();
            }
}

上面用到三个方法,一个是Uri解析路径uri2path();,一个是copyFile();用于复制图片到新的路径。 一个是调用系统裁剪工具startBigPhotoCrop();.

前面说了。4.4版本前后的到图片的Uri是不一样的。解析Uri路径需要处理。
给出处理方案的链接

为什么要复制图片?进行裁剪时,需要传入一个Uri,告诉它你要裁剪的是哪张图片,如果你不复制,裁剪的将是你的原图。你的一张美美的自拍照可不想就这么被摧毁了吧?当然,如果你的本意就是裁剪原图,可以忽略复制图片这一步了。

调用裁剪工具:

//裁剪大图片
    public void startBigPhotoCrop(Uri uri, int width, int height) {
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "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);
        intent.putExtra("return-data", false);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true); // no face detection
        mActivity.startActivityForResult(intent, PICTURE_CUT);
    }
上图的各种参数

上图的各个参数解释的很清楚了。这里有个参数"return-data" 是否返回数据。
建议,裁剪高分辨率图片,使用Uri不返回数据,小图使用Bitmap并返回数据。

转载请注明出处:http://blog.csdn.net/a3382589/article/details/52119360

  • 引用解释

在Android中,Intent触发Camera程序,拍好照片后,将会返回数据,但是考虑到内存问题,Camera不 会将全尺寸的图像返回给调用的Activity,一般情况下,有可能返回的是缩略图,比如120160px。
这是为什么呢?这不是一个Bug,而是经过精心设计的,却对开发者不透明。
以我的小米手机为例,摄像头800W像素,根据我目前设置拍出来的图片尺寸为3200
2400px。有人说,那就返回呗,大不了耗1-2M的内存,不错,这个尺寸的图片确实只有1.8M左右的大小。但是你想不到的是,这个尺寸对应的Bitmap会耗光你应用程序的所有内存。Android出于安全性考虑,只会给你一个寒碜的缩略图。
在Android2.3中,默认的Bitmap为32位,类型是ARGB_8888,也就意味着一个像素点占用4个字节的内存。我们来做一个简单的计算题:
320024004 bytes = 30M
如此惊人的数字!哪怕你愿意为一张生命周期超不过10s的位图愿意耗费这么巨大的内存,Android也不会答应的。
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,本着土财主般挥金如土的霸气才能做到。

处理裁剪的图片

 else if (CropPicUtil.PICTURE_CUT == requestCode && data != null && resultCode == -1) {
 //裁剪成功后返回
            bitmap = null;
            Bundle bundle = data.getExtras();
            if (bundle != null) {
                bitmap = bundle.getParcelable("data");
            }
            Log.e(TAG, "is bitmap null?" + (bitmap == null));
            if (bitmap == null) {
                try {
                    bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), tempUri);
                } catch (IOException e) {
                    e.printStackTrace();
                    Log.e(TAG, "onActivityResult: " + e);
                }
            }
            if (bitmap != null) {
                ...处理
            }
}

先获取返回的数据。若为null,选择的是使用Uri,否则是使用bitmap并返回数据,最后都是使用bitmap进行图片处理。

以上是调用系统图库来获取图片,进行裁剪后做图片处理的方案。

打开相机拍照获取图片相对来说简单一点,后面的裁剪的流程都一样,就不说了。

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.putExtra(MediaStore.EXTRA_OUTPUT, takePotoUri);
                startActivityForResult(intent, TAKE_PICTURE);

相对于通过图库获取图片的方式来说。拍照不需要进行版本判断,因为图片的路径是自己设定的。我们通过Intent将自己设定的路径转换为Uri传给系统相机,告诉它拍完的照片放在指定的路径。然后还是通过onActivityResult获取返回成功或失败的返回状态,做下一步的图片处理。

else if (TAKE_PICTURE == requestCode && resultCode == RESULT_OK) {
            CropPicPath = MyApplication.BasePath + System.currentTimeMillis() + ".jpg";
            tempUri = Uri.fromFile(new File(CropPicPath));
            FileUtil.copyFile(takePotoPath, CropPicPath);
            cropPicUtil.startSmallPhotoCrop(tempUri, 240, 240);
        }

上面贴出的全部都是思路。并没有完整的代码。杜绝伸手党。

本以为这样就可以收工了,后来发现6.0+的手机在获取图片的时候崩了。Google了之后原来android6.0以后的版本,权限不是一开始就询问是否授权了。而是运行的时候用到了才问。

android的权限系统一直是首要的安全概念,因为这些权限只在安装的时候被询问一次。一旦安装了,app可以在用户毫不知晓的情况下访问权限内的所有东西。
难怪一些坏蛋利用这个缺陷恶意收集用户数据用来做坏事了!
android小组也知道这事儿。7年了!权限系统终于被重新设计了。在android6.0棉花糖,app将不会在安装的时候授予权限。取而代之的是,app不得不在运行时一个一个询问用户授予权限。

too young too simple!要成为一名合格的Android程序猿,第一时间了解Android最新的变动消息还是很有必要的。

简单的解决方式就是在调用图库时请求权限

private static String[] PERMISSIONS_STORAGE = {
            Manifest.permission.READ_EXTERNAL_STORAGE,
            Manifest.permission.WRITE_EXTERNAL_STORAGE
    };
public static final int REQUEST_EXTERNAL_STORAGE = 100;

//使用图库或相机时调用
public void verifyStoragePermissions(Activity activity) {
        // Check if we have write permission
        int permission = ActivityCompat.checkSelfPermission(activity, Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission != PackageManager.PERMISSION_GRANTED) {
            // We don't have permission so prompt the user
            ActivityCompat.requestPermissions(
                    activity,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }
    }

但作为有追求的工程师,就应该完美的解决问题。详细的解决方案见微凉一季 的这篇blog

还有一个问题就是在小米手机上,获取图片后不是每一张图片都能显示出来。正在积极解决...
如果你有有效的解决方案,欢迎留言,大家共同进步!

一个小小的菜鸟第一次发表blog。希望这篇blog能帮到你,同时也是为了做一下笔记,见证自己的成长之路。语言组织能力不强,写的不好请小声点喷。

参考过的blog:
Android大图片裁剪终极解决方案
Android 4.4从图库选择图片,获取图片路径并裁剪

Demo下载

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,482评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,377评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,762评论 0 342
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,273评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,289评论 5 373
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,046评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,351评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,988评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,476评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,948评论 2 324
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,064评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,712评论 4 323
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,261评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,264评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,486评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,511评论 2 354
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,802评论 2 345

推荐阅读更多精彩内容