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

作者:八怪不姓丑
链接:http://www.jianshu.com/p/e11a34e2ea4f
著作权归作者所有,本文经作者授权推送。

一、前言

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

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

二、操作流程

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值