Android 7.0 之拍照、图库图片裁剪适配

在Android 7.0以上,在相机拍照 , 调用在对图片裁剪上,可能会碰到以下一些错误:

主要是由于在Android 7.0以后,用了Content Uri 替换了原本的File Uri,故在targetSdkVersion=24的时候,部分 “`Uri.fromFile()“` 方法就不适用了。 **File Uri 与 Content Uri 的区别** - File Uri 对应的是文件本身的存储路径 - Content Uri 对应的是文件在Content Provider的路径 所以在android 7.0 以上,我们就需要将File Uri转换为 Content Uri。具体转换方法如下:

开始写入标识 以及 函数值

    protected static final int CHOOSE_PICTURE = 0;
    protected static final int TAKE_PICTURE = 1;
    private static final int CROP_SMALL_PICTURE = 2;
    private File tempFile;
    private Uri cropImageUri;

第一:首先我们设置 本地图库 与 相机的点击事件:

/**
     * 显示会话框,选择
     */
    public void showChoosePicDialog(View view) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle("设置头像");
        String[] items = { "选择本地照片", "拍照" };
        builder.setNegativeButton("取消", null);
        builder.setItems(items, new DialogInterface.OnClickListener() {

            @Override
            public void onClick(DialogInterface dialog, int which) {
                switch (which) {
                    case CHOOSE_PICTURE: // 选择本地照片
                        Intent openAlbumIntent = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
                        openAlbumIntent.setType("image/*");
                        startActivityForResult(openAlbumIntent, CHOOSE_PICTURE);
                        break;
                    case TAKE_PICTURE: // 拍照
                        takecamera();
                        break;
                }
            }
        });
        builder.create().show();
    }

第二步: 对我们调用系统相机

 /**
     * 相机7.0适配7.0 手机适配 文件存储地址
     */
    private void takecamera() {
        String status= Environment.getExternalStorageState();
        if(status.equals(Environment.MEDIA_MOUNTED))
        {
            tempFile=new File(getExternalCacheDir(),getPhotoFileName());//SD卡的应用关联缓存目录
            try {
                if(tempFile.exists()){
                    tempFile.delete();
                }
                tempFile.createNewFile();
            } catch (Exception e) {
                Toast.makeText(InfomationActivity.this, "没有找到储存目录",Toast.LENGTH_LONG).show();
            }
        }else {
            Toast.makeText(InfomationActivity.this, "没有储存卡",Toast.LENGTH_LONG).show();
        }

        Intent cameraintent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // 指定调用相机拍照后照片的储存路径
        Uri uri=null;
        if (Build.VERSION.SDK_INT >= 24) {
            uri = FileProvider.getUriForFile(InfomationActivity.this, BuildConfig.APPLICATION_ID + ".myFileProvider", tempFile);
        }else {
            uri = Uri.fromFile(tempFile);
        }
        cameraintent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
        startActivityForResult(cameraintent, TAKE_PICTURE);

    }
    //使用系统当前日期加以调整作为照片的名称
    private String getPhotoFileName() {
        Date date = new Date(System.currentTimeMillis());
        SimpleDateFormat dateFormat = new SimpleDateFormat("'IMG'_yyyyMMdd_HHmmss");
        return dateFormat.format(date) + ".jpg";
    }

第三步:返回调用回来的信息:

    /**
     * 手机拍摄返回结果
     * @param requestCode
     * @param resultCode
     * @param data
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
            switch (requestCode) {
                case TAKE_PICTURE:
                    if (resultCode == RESULT_OK) {
                        Uri uri=null;
                        if(Build.VERSION.SDK_INT >= 24) {
                            uri = FileProvider.getUriForFile(InfomationActivity.this,BuildConfig.APPLICATION_ID+".myFileProvider",tempFile);
                            startPhotoZoom(uri);
                        }else {
                            uri = Uri.fromFile(tempFile);
                            startPhotoZoom(uri);
                        }
                    }
                    break;
                case CHOOSE_PICTURE:
                    if (resultCode == RESULT_OK) {
                        startPhotoZoom(data.getData());
                    }
                    break;
                case CROP_SMALL_PICTURE:
                    if(resultCode==RESULT_OK) {
                        if (data != null) {
                            try {
                                Bitmap bp = BitmapFactory.decodeStream(getContentResolver().openInputStream(cropImageUri));
                                circleIcon.setImageBitmap(bp);
                                uploadPic(bp);
                            } catch (FileNotFoundException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                    break;
            }

    }


第四步:手机图片裁切

/**
     * 7.0 适配7.0 以上手机 裁切图片
     * @param
     */
    private void startPhotoZoom(Uri uri) {
        File CropPhoto=new File(getExternalCacheDir(),"crop.jpg");
        try{
            if(CropPhoto.exists()){
                CropPhoto.delete();
            }
            CropPhoto.createNewFile();
        }catch(IOException e){
            e.printStackTrace();
        }

        cropImageUri=Uri.fromFile(CropPhoto);
        Intent intent = new Intent("com.android.camera.action.CROP");
        intent.setDataAndType(uri, "image/*");
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); //添加这一句表示对目标应用临时授权该Uri所代表的文件
        }
        // 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
        intent.putExtra("crop", "true");
        intent.putExtra("scale", true);

        intent.putExtra("aspectX", 1);
        intent.putExtra("aspectY", 1);
        //输出的宽高
        intent.putExtra("outputX", 200);
        intent.putExtra("outputY", 200);

        intent.putExtra("return-data", false);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, cropImageUri);
        intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
        intent.putExtra("noFaceDetection", true); // no face detection
        startActivityForResult(intent, CROP_SMALL_PICTURE);
    }

第五步:上传服务器 我这Retrofit+Rxjava上传上传不是File 而是 MultipartBody.Part body :

具体就 RequestBody 

 //创建RequestBody 封装file参数
            RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
            //创建RequestBody 设置类型等
            MultipartBody.Part requestBody = MultipartBody.Part.createFormData("icon", file.getName(), fileBody);

  /**
     * 上传到服务器
     * @param bitmap
     */
    private void uploadPic(Bitmap bitmap) {
        // ... 可以在这里把Bitmap转换成file,然后得到file的url,做文件上传操作
        // 注意这里得到的图片已经是圆形图片了
        // bitmap是没有做个圆形处理的,但已经被裁剪了
        String imagePath = ImageWzqUtils.savePhoto(bitmap, Environment
                .getExternalStorageDirectory().getAbsolutePath(), String
                .valueOf(System.currentTimeMillis()));
        Log.e("imagePath", imagePath+"");
        if(imagePath != null){
            // 拿着imagePath上着imagePath上传了
            // Bitmap bm = BitmapFactory.decodeFile(imagePath);
            File file=new File(imagePath);
            OkHttpClient okHttpClient=new OkHttpClient();
            //创建RequestBody 封装file参数
            RequestBody fileBody = RequestBody.create(MediaType.parse("multipart/form-data"), file);
            //创建RequestBody 设置类型等
            MultipartBody.Part requestBody = MultipartBody.Part.createFormData("icon", file.getName(), fileBody);

            persenter.getUpdatePic(requestBody);

        }

    }

这是因为拍照存储的文件,也需要以Content Uri的形式,故采用以下办法解决:

Step.1

修改AndroidManifest.xml

<application
    ...>
    <provider
       
  <!--Android中解决7.0权限FileUriPermission-->
        <provider
            android:name="android.support.v4.content.FileProvider"
            android:authorities="${applicationId}.myFileProvider"
            android:exported="false"
            android:grantUriPermissions="true">
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/file_paths" />
        </provider>
</application>

Step.2

在res/xml/下新建file_paths.xml文件

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
        <root-path name="root" path="." />
        <files-path name="files" path="." />
        <cache-path name="cache" path="." />
        <external-path name="external" path="." />
        <external-files-path name="external_files" path="." />
        <external-cache-path name="external_cache" path="." />
        <external-path name="external_files" path="."/>
</paths>


另外我也b把7.0的FileUriPermissCompat这个也分享一下:

public class FileUriPermissionCompat {

    private static final String TAG = FileUriPermissionCompat.class.getSimpleName();
    //此处需要改成AndroidManifest.xml中申请的对应的provider的值
    private static final String AUTHORITIES = BuildConfig.APPLICATION_ID +".myFileProvider";

    /**
     * 是否需要适配7.0权限
     * @return
     */
    public static boolean isNeedAdapt() {
        //24以上版本
        return Build.VERSION.SDK_INT >= Build.VERSION_CODES.N;
    }

    //适应Uri授权权限
    public static Uri adaptUriAndGrantPermission(Context context, Intent intent, File file) {
        Uri uri = adaptUri(context, file);
        if (uri == null) {
            return null;
        }
        grantUriPermission(context, intent, uri);
        return uri;
    }

    public static Uri adaptUri(Context context, File file) {
        if (context == null || file == null) {
            return null;
        }
        //网络路径的特殊处理,不需要7.0适配,但必须用parse()方法
        if (file.getPath().startsWith("http")) {
            return Uri.parse(file.getPath());
        }
        Uri uri = null;
        try {
            if (isNeedAdapt()) {
                //需要7.0特殊适配;通过系统提供的FileProvider类创建一个content类型的Uri对象
                uri = FileProvider.getUriForFile(context, AUTHORITIES, file);
            } else {
                //不需要适配
                uri = Uri.fromFile(file);
            }
        } catch (Exception e) {
            Log.e(TAG, "authorities value error, so can't convert uri !");
            e.printStackTrace();
        }
        return uri;
    }

    /**
     * 对第三方应用赋予对uri读写的权限
     * @param context
     * @param intent
     * @param saveUri 适配后的uri
     */
    public static void grantUriPermission(Context context, Intent intent, Uri saveUri) {
        if (!isNeedAdapt()) {
            return;
        }
        if (context == null || intent == null || saveUri == null) {
            return;
        }
        //网络路径的特殊处理,不需要权限
        if (saveUri.getScheme() != null && saveUri.getScheme().startsWith("http")) {
            //不需要授权
            return;
        }
        //1、授权(系统相册、相机、裁剪时需要)  -- 这种写法待分析
        List<ResolveInfo> resolveInfos = context.getPackageManager().queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
        for (ResolveInfo resolveInfo : resolveInfos) {
            String packageName = resolveInfo.activityInfo.packageName;
            if (TextUtils.isEmpty(packageName)) {
                continue;
            }
            context.grantUriPermission(packageName, saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        }
        //2、授权(安装apk时需要)
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
    }

    /**
     * 取消URi权限
     * @param context
     * @param intent
     * @param saveUri
     */
    public static void revokeUriPermission(Context context, Intent intent, Uri saveUri) {
        if (!isNeedAdapt()) {
            return;
        }
        if (context == null || intent == null || saveUri == null) {
            return;
        }
        //网络路径的特殊处理,不需要权限
        if (saveUri.getScheme() != null && saveUri.getScheme().startsWith("http")) {
            //不需要授权
            return;
        }
        try {
            context.revokeUriPermission(saveUri, Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
目前也就这么多。搞定了7.0手机截图以及拍照适配。




猜你喜欢

转载自blog.csdn.net/W2316/article/details/80893218