由于最近开发的一款app需要调用系统的照相机功能和获取相册中图片,在做手机版本适配7.0适配的时候,遇到一个大坑,拍照之后的照片,从相册中获取不到,最后Debug测试才知道获取的uri路径不对,最后使用Fileprovider解决问题.这里还讲图片进行了压缩的处理,这样更加节省性能的消耗!!!!!!!!!!
废话不多说了直接上代码,如需要完整的代码可以在我的github上下载,地址如下:
https://github.com/HelloWmz/PhotoDemo
使用Fileprovider之前需要在mainfest文件中配置配置如下:
<provider android:name="android.support.v4.content.FileProvider" android:authorities="applicationId.fileprovider" android:exported="false" android:grantUriPermissions="true" > <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider>
这里依赖的是v4包下的FileProviderapplicationId.fileprovider 配置这个属性的时候要与FileProvider.getUriForFile(this, "applicationId.fileprovider", photoFile);方法中的要一直可以随意写@xml/file_paths需要在res\xml\file_paths创建代码如下:
<?xml version="1.0" encoding="utf-8"?> <paths xmlns:android="http://schemas.android.com/apk/res/android"> <external-path path="" name="camera_photos" /> <external-path name="my_images" path="Android/data/项目的包名/files/Pictures/" /> <external-path name="images" path="Pictures/" /> <external-path name="dcim" path="DCIM/" /> </paths>
所以这篇文章写下自己当时遇到的坑,完整代码如下:
//这里mainactivity的布局就不粘贴了,就是两个button调用照相和获取相册中照片
public class MainActivity extends AppCompatActivity implements View.OnClickListener { private static final int SUCCESSCODE = 100; public Button mButton1; public Button mButton2; private String mPublicPhotoPath; private static final int REQ_GALLERY = 333; private static final int REQUEST_CODE_PICK_IMAGE = 222; public ImageView mImageView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); mButton1 = ((Button) findViewById(R.id.bt1)); mButton2 = ((Button) findViewById(R.id.bt2)); mButton1.setOnClickListener(this); mButton2.setOnClickListener(this); mImageView = ((ImageView) findViewById(R.id.iv)); } @Override public void onClick(View v) { switch (v.getId()) { //获取相册中的照片 case R.id.bt1: getImageFromAlbum(); break; //拍照功能 case R.id.bt2: showTakePicture(); break; } } /** * 获取相册中的图片 */ public void getImageFromAlbum() { Intent intent = new Intent(Intent.ACTION_PICK); intent.setType("image/*");//相片类型 startActivityForResult(intent, REQUEST_CODE_PICK_IMAGE); } //拍照的功能 private void showTakePicture() { PermissionGen.with(MainActivity.this) .addRequestCode(SUCCESSCODE) .permissions( Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE ) .request(); } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults); } //权限申请成功 @PermissionSuccess(requestCode = SUCCESSCODE) public void doSomething() { //申请成功 startTake(); } @PermissionFail(requestCode = SUCCESSCODE) public void doFailSomething() { } private void startTake() { Intent takePictureIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); //判断是否有相机应用 if (takePictureIntent.resolveActivity(getPackageManager()) != null) { //创建临时图片文件 File photoFile = null; try { photoFile = PictureUtils.createPublicImageFile(); mPublicPhotoPath = photoFile.getAbsolutePath(); } catch (IOException e) { e.printStackTrace(); } //设置Action为拍照 if (photoFile != null) { takePictureIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE); //这里加入flag takePictureIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); Uri photoURI = FileProvider.getUriForFile(this, "applicationId.fileprovider", photoFile); takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, photoURI); startActivityForResult(takePictureIntent, REQ_GALLERY); } } } private Uri uri; String path; int mTargetW; int mTargetH; @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); mTargetW = mImageView.getWidth(); mTargetH = mImageView.getHeight(); switch (requestCode) { //拍照 case REQ_GALLERY: if (resultCode != Activity.RESULT_OK) return; uri = Uri.parse(mPublicPhotoPath); path = uri.getPath(); PictureUtils.galleryAddPic(mPublicPhotoPath, this); break; //获取相册的图片 case REQUEST_CODE_PICK_IMAGE: if (data == null) return; uri = data.getData(); int sdkVersion = Integer.valueOf(Build.VERSION.SDK); if (sdkVersion >= 19) { path = this.uri.getPath(); path = PictureUtils.getPath_above19(MainActivity.this, this.uri); } else { path = PictureUtils.getFilePath_below19(MainActivity.this, this.uri); } break; } mImageView.setImageBitmap(PictureUtils.getSmallBitmap(path, mTargetW, mTargetH)); } }
/** * 图片处理的工具类 */ public class PictureUtils { /** * API19以下获取图片路径的方法 * * @param uri */ public static String getFilePath_below19(Context context, Uri uri) { //这里开始的第二部分,获取图片的路径:低版本的是没问题的,但是sdk>19会获取不到 String[] proj = {MediaStore.Images.Media.DATA}; //好像是android多媒体数据库的封装接口,具体的看Android文档 Cursor cursor = context.getContentResolver().query(uri, proj, null, null, null); //获得用户选择的图片的索引值 int column_index = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA); //将光标移至开头 ,这个很重要,不小心很容易引起越界 cursor.moveToFirst(); //最后根据索引值获取图片路径 结果类似:/mnt/sdcard/DCIM/Camera/IMG_20151124_013332.jpg String path = cursor.getString(column_index); return path; } /** * APIlevel 19以上才有 * 创建项目时,我们设置了最低版本API Level,比如我的是10, * 因此,AS检查我调用的API后,发现版本号不能向低版本兼容, * 比如我用的“DocumentsContract.isDocumentUri(context, uri)”是Level 19 以上才有的, * 自然超过了10,所以提示错误。 * 添加 @TargetApi(Build.VERSION_CODES.KITKAT)即可。 * * @param context * @param uri * @return */ @TargetApi(Build.VERSION_CODES.KITKAT) public static String getPath_above19(final Context context, final Uri uri) { final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT; // DocumentProvider if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) { // ExternalStorageProvider if (isExternalStorageDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; if ("primary".equalsIgnoreCase(type)) { return Environment.getExternalStorageDirectory() + "/" + split[1]; } } // DownloadsProvider else if (isDownloadsDocument(uri)) { final String id = DocumentsContract.getDocumentId(uri); final Uri contentUri = ContentUris.withAppendedId( Uri.parse("content://downloads/public_downloads"), Long.valueOf(id)); return getDataColumn(context, contentUri, null, null); } // MediaProvider else if (isMediaDocument(uri)) { final String docId = DocumentsContract.getDocumentId(uri); final String[] split = docId.split(":"); final String type = split[0]; Uri contentUri = null; if ("image".equals(type)) { contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI; } else if ("video".equals(type)) { contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI; } else if ("audio".equals(type)) { contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI; } final String selection = "_id=?"; final String[] selectionArgs = new String[]{ split[1] }; return getDataColumn(context, contentUri, selection, selectionArgs); } } // MediaStore (and general) else if ("content".equalsIgnoreCase(uri.getScheme())) { // Return the remote address if (isGooglePhotosUri(uri)) return uri.getLastPathSegment(); return getDataColumn(context, uri, null, null); } // File else if ("file".equalsIgnoreCase(uri.getScheme())) { return uri.getPath(); } return null; } /** * Get the value of the data column for this Uri. This is useful for * MediaStore Uris, and other file-based ContentProviders. * * @param context The context. * @param uri The Uri to query. * @param selection (Optional) Filter used in the query. * @param selectionArgs (Optional) Selection arguments used in the query. * @return The value of the _data column, which is typically a file path. */ public static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) { Cursor cursor = null; final String column = "_data"; final String[] projection = {column}; try { cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null); if (cursor != null && cursor.moveToFirst()) { final int index = cursor.getColumnIndexOrThrow(column); return cursor.getString(index); } } finally { if (cursor != null) cursor.close(); } return null; } /** * @param uri The Uri to check. * @return Whether the Uri authority is ExternalStorageProvider. */ public static boolean isExternalStorageDocument(Uri uri) { return "com.android.externalstorage.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is DownloadsProvider. */ public static boolean isDownloadsDocument(Uri uri) { return "com.android.providers.downloads.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is MediaProvider. */ public static boolean isMediaDocument(Uri uri) { return "com.android.providers.media.documents".equals(uri.getAuthority()); } /** * @param uri The Uri to check. * @return Whether the Uri authority is Google Photos. */ public static boolean isGooglePhotosUri(Uri uri) { return "com.google.android.apps.photos.content".equals(uri.getAuthority()); } /** * 图片的压缩 * * @param options * @param reqWidth * @param reqHeight * @return */ public static int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) { final int height = options.outHeight; final int width = options.outWidth; int inSampleSize = 1; if (height > reqHeight || width > reqWidth) { final int heightRatio = Math.round((float) height / (float) reqHeight); final int widthRatio = Math.round((float) width / (float) reqWidth); inSampleSize = heightRatio < widthRatio ? heightRatio : widthRatio; } return inSampleSize; } /** * 根据路径获得照片并压缩返回bitmap用于显示 * * @param filePath * @return */ public static Bitmap getSmallBitmap(String filePath, int reqWidth, int reqHeight) { final BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; //只返回图片的大小信息 BitmapFactory.decodeFile(filePath, options); // Calculate inSampleSize options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight); // Decode bitmap with inSampleSize set options.inJustDecodeBounds = false; return BitmapFactory.decodeFile(filePath, options); } /** * 将照片添加到相册中 */ public static void galleryAddPic(String mPublicPhotoPath, Context context) { Intent mediaScanIntent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE); File f = new File(mPublicPhotoPath); Uri contentUri = Uri.fromFile(f); mediaScanIntent.setData(contentUri); context.sendBroadcast(mediaScanIntent); } /** * 创建临时图片存储的路径 * * @return * @throws IOException */ public static File createPublicImageFile() throws IOException { File appDir = new File(Environment.getExternalStorageDirectory() + "/自定义相册的名字"); if (!appDir.exists()) { appDir.mkdir(); } String fileName = System.currentTimeMillis() + ".jpg"; File file = new File(appDir, fileName); return file; } }
这样就可以完美的解决此bug,这里我做了在给imageView添加图片之前先做了压缩的处理.希望这篇文章对你有所帮助,下次有时间将写写沉浸式状态栏的文章,能顶一个!!!!!!!!