Android调用系统相机、图库、裁剪图片并压缩上传(适配7.0)

一、前言

最近在开发中遇到了一个比较棘手的问题
由于在之前使用的版本-targetSdkVersion小于24也就是小于7.0所以在使用相机拍照的时候不会出现问题,但是当targetSdkVersion版本大于或者等于7.0的时候用原来的方法调用相机就会抛出一个SecurityException安全异常

通过搜索发现是出于对系统安全的考虑,在sdk24及以上,对相机的操作需要使用FileProvider才行。
虽然有些麻烦,但除非用第三方框架,不然也只能自己动手去解决了。

二、操作流程

image.png

1、定义全局标识

用于接收图库选择或拍照完成后的结果回调

    //图库
    private static final int PHOTO_TK = 0;
    //拍照
    private static final int PHOTO_PZ = 1;
    //裁剪
    private static final int PHOTO_CLIP = 2;

定义全局的uri

private Uri contentUri;

2、图库操作

这里用的是一个自定义的dialog


update_dialog_TK.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                //调用系统图库,选择图片
                Intent intent = new Intent(Intent.ACTION_PICK, null);
                intent.setDataAndType(
                        MediaStore.Images.Media.INTERNAL_CONTENT_URI, "image/*");
                //返回结果和标识
                startActivityForResult(intent, PHOTO_TK);
                dialog.dismiss();
            }
        });

3、相机操作

3.1 Android7.0以下版本

直接调用系统相机,通过日志可以看到会返回一个类似
file:///storage/emulated/0/temp.jpg的文件

update_dialog_PZ.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 启动系统相机
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                // 获取拍完后的uri
                Uri mImageCaptureUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "temp.jpg"));
                intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
                //  返回结果和标识
                startActivityForResult(intent, PHOTO_PZ);
                dialog.dismiss();
            }
        });
3.2 兼容Android7.0以上版本

在新的版本中,Android对内容提供者做了限制,返回的不再是uri,而需要一个FileProvider
使用 content://代替了 file:///
所以如果直接使用原来的方法就会报错。
所以在之前我们要给AndroidManifest文件中application标签添加权限

<provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="你的包名.fileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/provider_paths" />
        </provider>

并且需要创建一个xml
(可以在上面android:resource="@xml/provider_paths"直接使用快捷键alt+enter创建,然后将代码拷贝进去)
external-path标签用来指定Uri共享,name属性的值可以自定义,path属性的值表示共享的具体位置

<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path name="external_files" path="."/>
</paths>

然后在调用系统相机的时候判断

update_dialog_PZ.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // 启动系统相机
                Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
                Uri mImageCaptureUri;
                // 判断7.0android系统
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                    //临时添加一个拍照权限
                    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                    //通过FileProvider获取uri
                    contentUri = FileProvider.getUriForFile(UpdatePhotoActivity.this,
                            "你的包名.fileProvider",
                            new File(Environment.getExternalStorageDirectory(), "temp.jpg"));
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
                } else {
                    mImageCaptureUri = Uri.fromFile(new File(Environment.getExternalStorageDirectory(), "temp.jpg"));
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageCaptureUri);
                }
                startActivityForResult(intent, PHOTO_PZ);
                dialog.dismiss();
            }
        });

4、 onActivityResult

使用onActivityResult接收操作完成的回调

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (resultCode == Activity.RESULT_OK) {
            switch (requestCode) {
                case PHOTO_PZ:
                  //获取拍照结果,执行裁剪
                   Uri pictur;
                  //如果是7.0android系统,直接获取uri
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                        pictur = contentUri;
                    } else {
                        pictur = Uri.fromFile(new File(
                                Environment.getExternalStorageDirectory() + "/temp.jpg"));
                    }
                    startPhotoZoom(pictur);
                    break;
                case PHOTO_TK:
                    //获取图库结果,执行裁剪
                    startPhotoZoom(data.getData());
                    break;  
                case PHOTO_CLIP:
                    //裁剪完成后的操作,上传至服务器或者本地设置
                    break;
            }
        }
    }

5、裁剪

当拍照完成后或者本地选择图片完毕之后会执行该方法。同时也做了7.0适配

  /**
     * 裁剪图片的方法.
     * 用于拍照完成或者选择本地图片之后
     */
    private Uri uritempFile;

    public void startPhotoZoom(Uri uri) {
        Log.e("uri=====", "" + uri);
        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", 60);
        intent.putExtra("outputY", 60);
        //uritempFile为Uri类变量,实例化uritempFile
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            //开启临时权限
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION | Intent.FLAG_GRANT_READ_URI_PERMISSION);
            //重点:针对7.0以上的操作
            intent.setClipData(ClipData.newRawUri(MediaStore.EXTRA_OUTPUT, uri));
            uritempFile = uri;
        } else {
            uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + "small.jpg");
        }
        intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
        intent.putExtra("return-data", false);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true);
        startActivityForResult(intent, PHOTO_CLIP);
    }

三、裁剪后的处理

在onActivityResult方法还有一个最后的处理,前面只是在给图片做操作,下面是将完成后的图片选择加载到本地或者上传到项目的服务端,一般在实际开发中用的比较多

1、加载至本地

使用Picasso加载,因为Picasso支持Uri、File、Stirng类型,不需要做进一步的转换就可以直接用。

Picasso.with(this)
                 .load(uritempFile)
                 .into(cardviewImg);

2、转成File并压缩、上传

在实际开发中有时候需要对图片进行进一步的处理,比如传到服务器需要File类型的文件,所以就需要进行再一次的转换。
具体可以根据需求来进行相应的操作


                        //裁剪后的图像转成BitMap
                        photo1 = BitmapFactory.decodeStream(getContentResolver().openInputStream(uritempFile));
                        //创建路径
                        String path = Environment.getExternalStorageDirectory()
                                .getPath() + "/Pic";
                        //获取外部储存目录
                        file = new File(path);
                        Log.e("file", file.getPath());
                        //创建新目录
                        file.mkdirs();
                        //以当前时间重新命名文件
                        long i = System.currentTimeMillis();
                        //生成新的文件
                        file = new File(file.toString() + "/" + i + ".png");
                        Log.e("fileNew", file.getPath());
                        //创建输出流
                        OutputStream out = new FileOutputStream(file.getPath());
                        //压缩文件,返回结果
                        boolean flag = photo1.compress(Bitmap.CompressFormat.JPEG, 100, out);

项目demo地址:https://github.com/wapchief/android-CollectionDemo


相关文章:
Android版本相机适配问题集合(不断整理更新中)

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

推荐阅读更多精彩内容