Android通过系统获取图片及截图

概述

日常开发当中,调用相机或者从相册中选择照片裁剪并上传是很常见的功能,虽然网上有很多框架,但是导入
别人的库, 无疑会增加App的体积和后期维护难度,因此这里讲一下如何使用系统自带的相机,相册,并裁剪。
这里是在Android7.0以上版本上运行。

第一步FileProvider准备

  1. 在AndroidManifest.xml中增加provider节点,代码如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    <provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="一般为包名"
    android:grantUriPermissions="true"
    android:exported="false">
    <meta-data
    android:name="android.support.FILE_PROVIDER_PATHS"
    android:resource="@xml/filepaths" />
    </provider>
    • android:authorities 表示授权列表,填写你的应用包名,当有多个授权时,用分号隔开;
    • android:exported 表示该内容提供器(ContentProvider)是否能被第三方程序组件使用,必须为false,
      否则会报异常:Java.lang.RuntimeException: Unable to get provider android.support.v4.content.FileProvider: java.lang.SecurityException: Provider must not be exported;
    • android:grantUriPermissions=”true” 表示授予 URI 临时访问权限;
    • android:resource 属性指向创建的xml文件的路径,文件名可以随便起;
  2. 在资源(res)目录下创建一个xml目录,并建立一个以上面名字(filepaths)为文件名的xml文件,内容如下:

    1
    2
    3
    4
    <?xml version="1.0" encoding="utf-8"?>
    <paths>
    <external-path path="." name="external_path" />
    </paths>
    • external-path 代表根目录为: Environment.getExternalStorageDirectory() ,也可以写其他的,
      如: files-path 代表根目录为:Context.getFilesDir()
      cache-path 代表根目录为:getCacheDir() 其path属性的值代表路径后层级名称,为空则代表就是根目录,
      假如为“pictures”,就代表对应根目录下的pictures目录

第二步使用FileProvider

⚠️在使用FileProvider前需要在需要在AndroidManifest.xml中增加必要的读写权限:

1
2
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>

  1. 通过相机获取图片

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    /**
    * 拍照
    */
    private void takePhoto() {
    //用于保存调用相机拍照后所生成的文件
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    return;
    }
    captureFile = new File(rootFile, "temp.jpg");
    //跳转到调用系统相机
    Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    //判断版本 如果在Android7.0以上,使用FileProvider获取Uri
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
    intent.setFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    Uri contentUri = FileProvider.getUriForFile(mContext, getPackageName(), captureFile);
    intent.putExtra(MediaStore.EXTRA_OUTPUT, contentUri);
    } else {
    //否则使用Uri.fromFile(file)方法获取Uri
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(captureFile));
    }
    startActivityForResult(intent, REQUEST_PERMISSION_CAMERA);
    }
  2. 通过相册获取图片

    1
    2
    3
    4
    5
    6
    7
    8
    /**
    * 从相册选择
    */
    private void choosePhoto() {
    Intent photoPickerIntent = new Intent(Intent.ACTION_PICK);
    photoPickerIntent.setType("image/*");
    startActivityForResult(photoPickerIntent, REQUEST_PERMISSION_WRITE);
    }
  3. 图片剪裁

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21

    private void cropPhoto(Uri uri) {
    cropFile = new File(rootFile, "avatar.jpg");
    Intent intent = new Intent("com.android.camera.action.CROP");
    intent.setDataAndType(uri, "image/*");
    //crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
    intent.putExtra("crop", "true");
    //aspectX aspectY 是宽高的比例
    intent.putExtra("aspectX", 1);
    intent.putExtra("aspectY", 1);
    //outputX outputY 是裁剪图片宽高
    intent.putExtra("outputX", 300);
    intent.putExtra("outputY", 300);
    intent.putExtra("return-data", false);//注意这里返回false,因为在部分手机上获取不到返回的数据
    intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(cropFile));
    intent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
    intent.putExtra("noFaceDetection", true); //人脸识别
    intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    startActivityForResult(intent, CROP_REQUEST_CODE);
    }

第三部接收图片信息

在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
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
//拍照后逻辑
case REQUEST_PERMISSION_CAMERA:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
Uri contentUri = FileProvider.getUriForFile(mContext, getPackageName(), captureFile);
cropPhoto(contentUri);
} else {
cropPhoto(Uri.fromFile(captureFile));
}
break;
//图片获取后逻辑
case REQUEST_PERMISSION_WRITE:
cropPhoto(data.getData());
break;
//图片裁剪后逻辑
case CROP_REQUEST_CODE:
saveImage(cropFile.getAbsolutePath());
ivAvatar.setImageBitmap(BitmapFactory.decodeFile(cropFile.getAbsolutePath()));
break;
default:
break;
}
}
super.onActivityResult(requestCode, resultCode, data);
}

  • 保存图片在本地
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    if (!Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
    return null;
    }
    Bitmap bitmap = BitmapFactory.decodeFile(path);
    try {
    FileOutputStream fos = new FileOutputStream(cropFile);
    bitmap.compress(Bitmap.CompressFormat.JPEG, 100, fos);
    fos.flush();
    fos.close();
    return cropFile.getAbsolutePath();
    } catch (IOException e) {
    e.printStackTrace();
    }
    return null;
    }

⚠️在使用相机等动态权限时要注意权限的获取

参考:
[1] https://www.jianshu.com/p/0a2d8466fe6d
[2]7.0FileProvider使用 http://yifeng.studio/2017/05/03/android-7-0-compat-fileprovider/