Android调用系统相机、相册功能,适配6.0权限获取以及7.0以后获取URI(兼容多版本)

  Android中调用系统相机来拍摄照片的代码,如下:

1、首先设置Uri获取判断以及相机请求Code

[java]  view plain  copy
  1. public final int TYPE_TAKE_PHOTO = 1;//Uri获取类型判断  
  2.   
  3. public final int CODE_TAKE_PHOTO = 1;//相机RequestCode  

 2、调起系统相机

[java]  view plain  copy
  1. Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
  2. Uri photoUri = getMediaFileUri(TYPE_TAKE_PHOTO);  
  3. takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);  
  4. startActivityForResult(takeIntent, CODE_TAKE_PHOTO);  
3、封装获取Uri代码

[java]  view plain  copy
  1. public Uri getMediaFileUri(int type){  
  2.     File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "相册名字");  
  3.     if (!mediaStorageDir.exists()) {  
  4.         if (!mediaStorageDir.mkdirs()) {  
  5.             return null;  
  6.         }  
  7.     }  
  8.     //创建Media File  
  9.     String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());  
  10.     File mediaFile;  
  11.     if (type == TYPE_TAKE_PHOTO) {  
  12.         mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");  
  13.     } else {  
  14.         return null;  
  15.     }  
  16.     return Uri.fromFile(mediaFile);  
  17. }  

4、相机拍照完毕后获取返回数据,并在页面显示照片

[java]  view plain  copy
  1. @Override  
  2.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.         super.onActivityResult(requestCode, resultCode, data);  
  4.         switch (requestCode) {  
  5.             case CODE_TAKE_PHOTO:  
  6.                 if (resultCode == RESULT_OK) {  
  7.                     if (data != null) {  
  8.                         if (data.hasExtra("data")) {  
  9.                             Log.i("URI""data is not null");  
  10.                             Bitmap bitmap = data.getParcelableExtra("data");  
  11.                             imageView.setImageBitmap(bitmap);//imageView即为当前页面需要展示照片的控件,可替换  
  12.                         }  
  13.                     } else {  
  14.                         Log.i("URI""Data is null");  
  15.                         Bitmap bitmap = BitmapFactory.decodeFile(fileUri.getPath());  
  16.                         imageView.setImageBitmap(bitmap);//imageView即为当前页面需要展示照片的控件,可替换  
  17.                     }  
  18.                 }  
  19.                 break;  
  20.         }  
  21.     }  
  特殊:

      一般情况,以上代码在Android7.0以下,也就是api<24时,运行是没有任何问题的。可是当targetSdkVersion变成24及其以上并且在android7.0(及以上版本)系统运行时,会抛出异常:FileUriExposedException。

  权限:

      由于已经写过一篇文章对android6.0后权限设置和6.0以前权限设置进行过讲述,在此就不再赘述如何配置权限了,对于6.0后权限设置不了解的,可以看看:通俗易懂,手把手教会你android 6.0后(兼容6.0之前版本)申请危险权限的方法

  Android7.0及以上版本调用系统相机

1、为什么会报FileUriExposedException异常

  android N以后收回了访问文件的权限,按照android N的要求,若在应用间共享文件,需要发送Content://Uri,而不再是File://Uri,并且需要对此Uri进行临时访问授权。

2、解决办法:

  使用FileProvider,FileProvider是V4包下的类,继承自ContentProvider。

3、使用步骤:

  ①、首先在清单文件中进行FileProvider注册(与activity同级,四大组件没啥说的):

[html]  view plain  copy
  1. <provider  
  2.             android:name="android.support.v4.content.FileProvider"  
  3.             android:authorities="项目包名.fileprovider"  
  4.             android:exported="false"  
  5.             android:grantUriPermissions="true">  
  6.             <meta-data  
  7.                 android:name="android.support.FILE_PROVIDER_PATHS"  
  8.                 android:resource="@xml/provider_path" />  
  9.         </provider>  
  补充:android:exported="false"  表示对其他应用不可用
        android:grantUriPermissions="true"  授予临时权限

  ②、在res目录下新建xml文件夹,即res/xml,在xml中新建一个provider_path文件,如下:

[html]  view plain  copy
  1. <?xml version="1.0" encoding="utf-8"?>  
  2. <paths xmlns:android="http://schemas.android.com/apk/res/android">  
  3.     <external-path name="external_files" path=""/>  
  4. </paths>  
      补充:<paths>下一般常用以下子节点:

        <files-path>:Context.getFilesDir()——指向内部存储要共享的目录 

        <cache-path>:Context.getCacheDir()——指向缓存要共享的目录

        <external-path>:Environment.getExternalStorageDirectory()——指向外部存储要共享的目录 

        <root-path>:尚未发现官方对其的说明,知道的童鞋欢迎补充。根据字面理解,为整个存储的根路径,针对诸如查找不到照片地址的Bug。

        name为自定义的名字,path为目录,path=""指的是全部目录|path="."即为当前的根目录

  ③、只用更改上面贴出来的getMediaFileUri()方法,因为牵涉多版本调用相机,所以再封装一个适用于7.0以上获取Uri的方法get24MediaFileUri(),代码如下:

[java]  view plain  copy
  1. /** 
  2.      * 版本24以上 
  3.      */  
  4.     public Uri get24MediaFileUri(int type) {  
  5.         File mediaStorageDir = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES), "相册名字");  
  6.         if (!mediaStorageDir.exists()) {  
  7.             if (!mediaStorageDir.mkdirs()) {  
  8.                 return null;  
  9.             }  
  10.         }  
  11.         //创建Media File  
  12.         String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());  
  13.         File mediaFile;  
  14.         if (type == TYPE_TAKE_PHOTO) {  
  15.             mediaFile = new File(mediaStorageDir.getPath() + File.separator + "IMG_" + timeStamp + ".jpg");  
  16.         } else {  
  17.             return null;  
  18.         }  
  19.         return FileProvider.getUriForFile(this, getPackageName()+".fileprovider", mediaFile);  
  20.     }  

  注意:get24MediaFileUri()与getMediaFileUri()唯一的不同为:api24以下,使用的是Uri.fromFile(File)获取的Uri,api24及以上必须使用FileProvider,调用FileProvider.getUriForFile(this, getPackageName()+".fileprovider", File)来获取Uri。

  ④、7.0以上调用系统相机,如下:

[java]  view plain  copy
  1. Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
  2. Uri photoUri = get24MediaFileUri(TYPE_TAKE_PHOTO);  
  3. takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);  
  4. startActivityForResult(takeIntent, CODE_TAKE_PHOTO);  

  只是将Uri获取的途径由getMediaFileUri()改为了get24MediaFileUri()。

  ⑤、多版本适配

1、权限获取的适配,详细请看 通俗易懂,手把手教会你android 6.0后(兼容6.0之前版本)申请危险权限的方法

2、在权限获取后,判断SDK版本,然后进行相应操作,如下:

[java]  view plain  copy
  1. if (Build.VERSION.SDK_INT >= 24) {  
  2.         Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
  3.         Uri photoUri = get24MediaFileUri(TYPE_TAKE_PHOTO);  
  4.         takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);  
  5.         startActivityForResult(takeIntent, CODE_TAKE_PHOTO);  
  6.     } else {  
  7.         Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);  
  8.         Uri photoUri = getMediaFileUri(TYPE_TAKE_PHOTO);  
  9.         takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);  
  10.         startActivityForResult(takeIntent, CODE_TAKE_PHOTO);  
  11.     }  

  ⑥、拍照完毕后,获取返回数据显示照片,api24以后,需要转换输入流来获取Bitmap,而输入流的获取需要通过getContentResolvrer.openInputStream()来获取,具体代码如下:

[java]  view plain  copy
  1. @Override  
  2.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.         super.onActivityResult(requestCode, resultCode, data);  
  4.         switch (requestCode) {  
  5.             case CODE_TAKE_PHOTO:  
  6.                 if (resultCode == RESULT_OK) {  
  7.                     if (data != null) {  
  8.                         if (data.hasExtra("data")) {  
  9.                             Log.i("URI""data is not null");  
  10.                             Bitmap bitmap = data.getParcelableExtra("data");  
  11.                             imageView.setImageBitmap(bitmap);//imageView即为当前页面需要展示照片的控件,可替换  
  12.                         }  
  13.                     } else {  
  14.                         Log.i("URI""Data is null");  
  15.                          if (Build.VERSION.SDK_INT >= 24){  
  16.                             Bitmap bitmap = null;  
  17.                             try {  
  18.                                 bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(photoUri));  
  19.                             } catch (FileNotFoundException e) {  
  20.                                 e.printStackTrace();  
  21.                             }  
  22.                     imageView.setImageBitmap(bitmap);  
  23.                         }else {  
  24.                             Bitmap bitmap = BitmapFactory.decodeFile(photoUri.getPath());  
  25.                             imageView.setImageBitmap(bitmap);  
  26.                         }  
  27.                     }  
  28.                 }  
  29.                 break;  
  30.         }  
  31.     }  
通过以上步骤,即可在多版本系统中调用系统相机并显示了。

  系统相册选择图片

1、设置相册请求Code

[java]  view plain  copy
  1. public final int CODE_SELECT_IMAGE = 2;//相册RequestCode  

2、调用系统相册

[java]  view plain  copy
  1. Intent albumIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);  
  2.                         startActivityForResult(albumIntent, CODE_SELECT_IMAGE);  

注意:相册在6.0后会用到访问存储设备这个危险权限,所以也得做权限适配

3、选择图片后,获取返回数据,并显示图片

[java]  view plain  copy
  1. @Override  
  2.     protected void onActivityResult(int requestCode, int resultCode, Intent data) {  
  3.         super.onActivityResult(requestCode, resultCode, data);  
  4.         switch (requestCode) {  
  5.             case CODE_SELECT_IMAGE:  
  6.                 if (resultCode == RESULT_OK) {  
  7.                     selectPic(data);  
  8.                 }  
  9.                 break;  
  10.         }  
  11.     }  
  12.       

[java]  view plain  copy
  1. //选择照片  
  2.     private void selectPic(Intent intent) {  
  3.         Uri selectImageUri = intent.getData();  
  4.         String[] filePathColumn = {MediaStore.Images.Media.DATA};  
  5.         Cursor cursor = getContentResolver().query(selectImageUri, filePathColumn, nullnullnull);  
  6.         cursor.moveToFirst();  
  7.         int columnIndex = cursor.getColumnIndex(filePathColumn[0]);  
  8.         String picturePath = cursor.getString(columnIndex);  
  9.         cursor.close();  
  10.         imageView.setImageBitmap(BitmapFactory.decodeFile(picturePath));  
  11.     }  

以上即可调用系统相册选择照片,并显示。


猜你喜欢

转载自blog.csdn.net/zjy_android/article/details/79286316