2020-12-25

Android 11®兼容拍照,系统裁剪图片路径问题

应用适配到android Q后,在android R测试发现拍照,图片裁剪后图片带不回来显示。具体使用场景描述:适配Android Q后,由于存储方式变成沙盒模式,导致之前的file://xxxxx或直接的文件路径,这都无法访问到文件。但系统的多媒体文件夹如果有存储权限还是可以访问到的。测试发现在一加手机系统是 R 上的,给拍照和裁剪指定的路径是无法存储起来的。HUAWEI Q系统的指定的路径如果是/storage/emulated/0/Android/data/自己应用包名/xxxx可以存储起来,否则就存储失败。其他低版本系统指定的存储路径多可以存储。

  • 针对上面问题的解决方法

通过ContentResolver来操作,实现一种比较通用的方法,不用去判断手机系统版本。可以覆盖4.4~11的系统

先来看看拍照

 String state = Environment.getExternalStorageState();
 Uri outputUri = null;
     if (!state.equals(Environment.MEDIA_MOUNTED)) {
                outputUri = activity.getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());
            } else {
                outputUri = activity.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
            }
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
            intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
            activity.startActivityForResult(intent, RequestCode.ACTIVITY_REQUEST_CODE_TAKE_PHOTO);`

通过contentresolver插入一条记录来生成outputUri,拍完照片outputUri就是图片的路径了。但如果没拍照直接返回就要删掉之前插入的这条记录。不然图库查看图片时可能会多出一张空图片。删除方式在onActivityResult()操作,如下代码

protected void onActivityResult(int requestCode, int resultCode, Intent data) {
		switch (requestCode) {
		case MediaUtils.RequestCode.ACTIVITY_REQUEST_CODE_TAKE_PHOTO:// 拍照
			if (resultCode == Activity.RESULT_OK) {
				starScan(mediaCaptureItem.imgUri, this);
				Intent intent = new Intent();
				intent.putExtra("capturePath", mediaCaptureItem);
				this.setResult(9999, intent);
				this.finish();

			}else{
				//没拍照返回,删除图片库中的图片
				int delete = getContentResolver().delete(outputUri, null, null);
			}
			break;
		default:
			break;
		}
	}

系统R有点我觉得挺有意思的,拍照裁剪如果不指定outputUri,也是可以自动生成一个Uri路径,但我们却删除不了,没有权限。深深的觉得安卓的管理越来越严了。
我们获得了outputUri后怎么显示图片,资料还是很多的自己搜,如有不了解的评论留言!

图片裁剪
打开系统裁剪,关于图片路径问题跟拍照一个样的。

 private void startPhotoZoom(Uri uri) {
        String state = Environment.getExternalStorageState();
        if (!state.equals(Environment.MEDIA_MOUNTED)) {//生成一个content://的图片路径,存放截图
            outputUri = getContentResolver().insert(MediaStore.Images.Media.INTERNAL_CONTENT_URI, new ContentValues());
        } else {
            outputUri = getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new ContentValues());
        }
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, IMAGE_UNSPECIFIED);
        intent.putExtra("crop", "true");
        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        intent.putExtra("outputX", DisplayUtils.getScreenWidth(mActivity));
        intent.putExtra("outputY", DisplayUtils.getScreenWidth(mActivity));
        intent.putExtra("scale", true);
        intent.putExtra("scaleUpIfNeeded", true);//去黑边
        intent.putExtra(MediaStore.EXTRA_OUTPUT, outputUri);
        intent.putExtra("return-data", false);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true); // no face detection
        startActivityForResult(intent, PHOTORESOULT);
    }

裁剪完成的回调

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
	   if (requestCode == PHOTORESOULT) {//裁剪头像图片成功
	            if (data != null) {
	                    mAdapter.addPhotoPath(outputUri +"");
	            }
	    }
	 }

因为我图片我是用glide加载的所以对于content://这种Uri转成String,是可以直接加载成功的。在使用裁剪需要注意的是,裁剪完成图库是有生成一张图片的,所以在使用完裁剪图片,如果不想让他显示在图库上,还是用*contentresolver.delete(uri,null,null)*删除掉。比如对图片压缩保存到自己的文件夹后。

在R,Q系统上,如果Uri使用起来不顺手的话,想转成真实的String路径。可以在onActivityResult()回调时把Uri转成String。但只针对多媒体文件。方法如下:

  public static String getFilePath(Context context, 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];
                }

                // TODO handle non-primary volumes
            }
            // 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 getDataColumn(context, uri, null, null);
        }
        // File
        else if ("file".equalsIgnoreCase(uri.getScheme())) {
            return uri.getPath();
        }

        return null;
    }
    
      public static boolean isMediaDocument(Uri uri) {
        return "com.android.providers.media.documents".equals(uri.getAuthority());
    }

    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());
    }

以上是自己的适配兼容方式,不敢说最好,但至少可以解决问题。希望可以帮到需要的人。如有疑问欢迎评论。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值