一、引入
- Android在7.0中修改了文件权限,所以从Android7.0开始要使用FileProvider来处理uri,从网上找了好多文章,解决了在7.0下拍照及相册选图的问题,但是参照网上的解决方案前切图片一直搞不定,最终使用了UCrop进行剪切图片并返回文件地址,便于与服务器交互。
- 本文主要介绍在Android7.0上进行拍照,相册选图以及相应的图片剪切,当然也会向下兼容,同时我也在Android4.3的手机上进行了测试,在文章最后我会附上源码,会有我自认为详细的注释哈哈。

二、拍照及相册
- FileProvider - 想必FileProvider大家都很熟悉了,但是想了一下感觉还是写一下比较好。 - 在manifest中配置 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14- <application 
 ... ...
 <provider
 android:name="android.support.v4.content.FileProvider"
 android:authorities="com.sdwfqin.sample.fileprovider"
 android:exported="false"
 android:grantUriPermissions="true">
 <meta-data
 android:name="android.support.FILE_PROVIDER_PATHS"
 android:resource="@xml/file_paths_public"/>
 </provider>
 </application>
- 在 res 目录下新建文件夹 xml 然后创建资源文件 file_paths_public(名字随意,但是要和manifest中的名字匹配) - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 <paths>
 <!--照片-->
 <external-path
 name="my_images"
 path="Pictures"/>
 <!--下载-->
 <paths>
 <external-path
 name="download"
 path=""/>
 </paths>
 </paths>
 
- 调用相机拍照 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35- // 全局变量 
 public static final int RESULT_CODE_1 = 201;
 // 7.0 以上的uri
 private Uri mProviderUri;
 // 7.0 以下的uri
 private Uri mUri;
 // 图片路径
 private String mFilepath = SDCardUtils.getSDCardPath() + "AndroidSamples";
 -----------
 /**
 * 拍照
 */
 private void camera() {
 File file = new File(mFilepath, System.currentTimeMillis() + ".jpg");
 if (!file.getParentFile().exists()) {
 file.getParentFile().mkdirs();
 }
 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 // Android7.0以上URI
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
 //通过FileProvider创建一个content类型的Uri
 mProviderUri = FileProvider.getUriForFile(this, "com.sdwfqin.sample.fileprovider", file);
 intent.putExtra(MediaStore.EXTRA_OUTPUT, mProviderUri);
 //添加这一句表示对目标应用临时授权该Uri所代表的文件
 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
 } else {
 mUri = Uri.fromFile(file);
 intent.putExtra(MediaStore.EXTRA_OUTPUT, mUri);
 }
 try {
 startActivityForResult(intent, RESULT_CODE_1);
 } catch (ActivityNotFoundException anf) {
 ToastUtils.showShort("摄像头未准备好!");
 }
 }
- 相册选图 - 1 
 2
 3
 4
 5
 6
 7
 8
 9- // 全局变量 
 public static final int RESULT_CODE_2 = 202;
 ----------
 private void selectImg() {
 Intent pickIntent = new Intent(Intent.ACTION_PICK,
 MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
 pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
 startActivityForResult(pickIntent, RESULT_CODE_2);
 }
- onActivityResult - 需要注意的是拍照没有返回数据,用之前的uri就可以,从相册查找图片会返回uri - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12- case RESULT_CODE_1: 
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
 // 调用裁剪方法
 cropRawPhoto(mProviderUri);
 } else {
 cropRawPhoto(mUri);
 }
 break;
 case RESULT_CODE_2:
 Log.i(TAG, "onActivityResult: " + data.getData());
 cropRawPhoto(data.getData());
 break;
三、图片剪裁(重点)
- 因为用原生的一直是各种报错,所以我这里用的是UCrop,大家可能都见过官方的展示图,界面可能在有些需求下显得过于复杂,但是真正使用起来感觉有很多都是可以修改的哈哈哈!推荐大家看一下官方的例子。项目地址:https://github.com/Yalantis/uCrop 
- 简单说一下引入方法但是并不能保证是最新的 - 依赖 - 1 - compile 'com.github.yalantis:ucrop:2.2.1' 
- 在AndroidManifest中添加Activity - 1 
 2
 3
 4- <activity 
 android:name="com.yalantis.ucrop.UCropActivity"
 android:screenOrientation="portrait"
 android:theme="@style/Theme.AppCompat.Light.NoActionBar"/>
 
- 剪切图片 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28- public void cropRawPhoto(Uri uri) { 
 // 修改配置参数(我这里只是列出了部分配置,并不是全部)
 UCrop.Options options = new UCrop.Options();
 // 修改标题栏颜色
 options.setToolbarColor(getResources().getColor(R.color.colorPrimary));
 // 修改状态栏颜色
 options.setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
 // 隐藏底部工具
 options.setHideBottomControls(true);
 // 图片格式
 options.setCompressionFormat(Bitmap.CompressFormat.JPEG);
 // 设置图片压缩质量
 options.setCompressionQuality(100);
 // 是否让用户调整范围(默认false),如果开启,可能会造成剪切的图片的长宽比不是设定的
 // 如果不开启,用户不能拖动选框,只能缩放图片
 options.setFreeStyleCropEnabled(true);
 // 设置源uri及目标uri
 UCrop.of(uri, Uri.fromFile(new File(mFilepath, System.currentTimeMillis() + ".jpg")))
 // 长宽比
 .withAspectRatio(1, 1)
 // 图片大小
 .withMaxResultSize(200, 200)
 // 配置参数
 .withOptions(options)
 .start(this);
 }
- 剪切完图片的回掉 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19- if (resultCode == UCrop.RESULT_ERROR){ 
 mCameraTv.setText(UCrop.getError(data) + "");
 showMsg("图片剪裁失败");
 return;
 }
 if (resultCode == RESULT_OK) {
 switch (requestCode) {
 case UCrop.REQUEST_CROP:
 // 成功(返回的是文件地址)
 Log.i(TAG, "onActivityResult: " + UCrop.getOutput(data));
 mCameraTv.setText(UCrop.getOutput(data) + "");
 // 使用Glide显示图片
 Glide.with(this)
 .load(UCrop.getOutput(data))
 .crossFade()
 .into(mCameraImg);
 break;
 }
 }
- 完整的onActivityResult,包含拍照的回掉 - 1 
 2
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
 super.onActivityResult(requestCode, resultCode, data);
 if (resultCode == UCrop.RESULT_ERROR){
 mCameraTv.setText(UCrop.getError(data) + "");
 showMsg("图片剪裁失败");
 return;
 }
 if (resultCode == RESULT_OK) {
 switch (requestCode) {
 case RESULT_CODE_1:
 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
 cropRawPhoto(mProviderUri);
 } else {
 cropRawPhoto(mUri);
 }
 break;
 case RESULT_CODE_2:
 Log.i(TAG, "onActivityResult: " + data.getData());
 cropRawPhoto(data.getData());
 break;
 case UCrop.REQUEST_CROP:
 Log.i(TAG, "onActivityResult: " + UCrop.getOutput(data));
 mCameraTv.setText(UCrop.getOutput(data) + "");
 Glide.with(this)
 .load(UCrop.getOutput(data))
 .crossFade()
 .into(mCameraImg);
 break;
 }
 }
 }