Android7.0拍照,读取相册,裁剪问题

首先看一下链接:Android7.0 完美适配——FileProvider 拍照裁剪全解析,这里面有关问题讲的很详细。 总之一句话在Android7.0之前拍照,读取相册,裁剪是可以通过file://Uri 来传递意图,之后便不被允许。解决办法是使用FileProvider操作。但是我按照文中所讲在小米7.0的手机和小米6.0的手机中不能完全适配。折腾很久之后才找到解决办法。如下: 首先在AndroidManifest中添加如下代码:

<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/provider_paths" />
        </provider>

其中“applicationId”表示的是包名,“fileprovider”是随意起的名称,在之后使用FileProvider.getUriForFile()方法时会用到。 之后在res目录下添加xml文件夹,里面添加paths文件,文件名是provider_paths,代码如下:

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!--        xml文件是唯一设置分享的目录 ,不能用代码设置

         1.<files-path>        getFilesDir()  /data/data//files目录
         2.<cache-path>        getCacheDir()  /data/data//cache目录

         3.<external-path>     Environment.getExternalStorageDirectory()

         SDCard/Android/data/你的应用的包名/files/ 目录
         4.<external-files-path>     Context#getExternalFilesDir(String) Context.getExternalFilesDir(null).
         5.<external-cache-path>      Context.getExternalCacheDir().
     -->

    <!--    path :代表设置的目录下一级目录 eg:<external-path path="images/"
                整个目录为Environment.getExternalStorageDirectory()+"/images/"
            name: 代表定义在Content中的字段 eg:name = "myimages" ,并且请求的内容的文件名为default_image.jpg
                则 返回一个URI   content://com.example.myapp.fileprovider/myimages/default_image.jpg
    -->
    <!--当path 为空时 5个全配置就可以解决-->
    <external-path name="external_files" path="/com.ooli/"/>
</paths>

上文中path里的路径要注意,本文使用的是HttpUrl.photoPath=Environment.getExternalStorageDirectory()+"/com.ooli";一定要对应好,不然在之后会因路径不对导致出错。 再之后便是在代码中添加拍照,读取相册,裁剪的逻辑,如下:

// 创建文件夹
            file = new File(HttpUrl.photoPath);
            if (!file.exists()) {
                file.mkdirs();
            }
            cemera_tv.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    if (hasSdcard()) {
                        //第二个参数是需要申请的权限
                        if (ContextCompat.checkSelfPermission(CenterOfUserActivity.this,
                                android.Manifest.permission.CAMERA)
                                != PackageManager.PERMISSION_GRANTED) {

                            //权限还没有授予,需要在这里写申请权限的代码
                /*
                第二个参数是一个字符串数组,里面是你需要申请的权限。既然是一个数组,那么就说明你一次可以申请多个权限。
				最后一个参数是一个整型常量,用于标志你这次申请的权限,该常量在onRequestPermissionsResult(…)方法中会用到。
				 */
                            ActivityCompat.requestPermissions(CenterOfUserActivity.this,
                                    new String[]{android.Manifest.permission.CAMERA}, Util.MY_PERMISSIONS_REQUEST_CALL_PHOTO);
                        } else {
                            //权限已经被授予,在这里直接写要执行的相应方法即可
                            startTakePhoto();
                        }
                    } else {
                        Util.showToast(CenterOfUserActivity.this, “存储卡不可用”);
                    }
                }
            });
            album_tv.setOnClickListener(new OnClickListener() {

                @Override
                public void onClick(View v) {
                    if (hasSdcard()) {
                        //判断是否有读写手机存储的权限
                        if (ContextCompat.checkSelfPermission(CenterOfUserActivity.this,
                                android.Manifest.permission.WRITE_EXTERNAL_STORAGE)
                                != PackageManager.PERMISSION_GRANTED) {
                            //权限还没有授予,需要在这里写申请权限的代码
                /*
                第二个参数是一个字符串数组,里面是你需要申请的权限。既然是一个数组,那么就说明你一次可以申请多个权限。
				最后一个参数是一个整型常量,用于标志你这次申请的权限,该常量在onRequestPermissionsResult(…)方法中会用到。
				 */
                            ActivityCompat.requestPermissions(CenterOfUserActivity.this,
                                    new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, Util.MY_PERMISSIONS_REQUEST_WRITE);
                        } else {
                            startphotoAlbum();
                        }
                    } else {
                        Util.showToast(CenterOfUserActivity.this, getResources().getString(R.string.sdcard_usable));
                    }
                }
            });
/**
     * 检查设备是否存在SDCard的工具方法
     */
    public boolean hasSdcard() {
        try {
            String state = Environment.getExternalStorageState();
            if (state.equals(Environment.MEDIA_MOUNTED)) {
                // 有存储的SDCard
                return true;
            }
        } catch (Exception e) {
            LogUtil.e(getClass(), "hasSdcard()", e);
        }
        return false;
    }
@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        //判断是拍照的权限
        if (requestCode == Util.MY_PERMISSIONS_REQUEST_CALL_PHOTO) {
            if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                startTakePhoto();
            } else {
                Util.intentPermission(CenterOfUserActivity.this, getResources().getString(R.string.allow_take_photo));
            }
        }
        //判断是读写手机存储的权限
        if (requestCode == Util.MY_PERMISSIONS_REQUEST_WRITE) {
            if (grantResults[0] != PackageManager.PERMISSION_GRANTED) {
                Util.intentPermission(CenterOfUserActivity.this, getResources().getString(R.string.allow_take_write));
            } else {
                startphotoAlbum();
            }
        }
    }

以上代码只是判断是否有权限, 第二步便是发起读取相册和拍照的代码:

 //读取相册
    private void startphotoAlbum() {
        try {
            Intent intentFromGallery = new Intent();
            // 设置文件类型
            intentFromGallery.setType("image/*");
            intentFromGallery.setAction(Intent.ACTION_GET_CONTENT);
            intentFromGallery.addCategory(Intent.CATEGORY_OPENABLE);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {//如果大于等于7.0使用FileProvider
                File outFile = new File(file, System.currentTimeMillis() + ".jpg");
                Uri uriForFile = FileProvider.getUriForFile(CenterOfUserActivity.this, getPackageName() + ".fileprovider", outFile);
                intentFromGallery.putExtra(MediaStore.EXTRA_OUTPUT, uriForFile);
                intentFromGallery.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                intentFromGallery.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
            }
            startActivityForResult(intentFromGallery,
                    Util.CODE_GALLERY_REQUEST);
        } catch (Exception e) {
            LogUtil.e(getClass(), "startphotoAlbum()", e);
        }
    }

    //执行拍照
    private void startTakePhoto() {
        try {
            Intent intentFromCapture = new Intent(
                    MediaStore.ACTION_IMAGE_CAPTURE);

            IMAGE_FILE_NAME = System.currentTimeMillis() + ".jpg";
            File outFile = new File(file, IMAGE_FILE_NAME);
            //如果该文件以经存在,则删除,否则创建一个
            if (outFile.exists()) {
                outFile.delete();
            }
            try {
                outFile.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
            Uri photoUri = null;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intentFromCapture.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intentFromCapture.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                photoUri = FileProvider.getUriForFile(CenterOfUserActivity.this, getPackageName() + ".fileprovider", outFile);
            } else {
                photoUri = Uri.fromFile(outFile);
            }
            intentFromCapture.putExtra(MediaStore.EXTRA_OUTPUT, photoUri);
            startActivityForResult(intentFromCapture,
                    Util.CODE_CAMERA_REQUEST);
        } catch (Exception e) {
            LogUtil.e(getClass(), "startTakePhoto()", e);
        }
    }

在这之后便是在onActivityResult()中接收照片,处理逻辑:

@Override
    protected void onActivityResult(int requestCode, int resultCode,
                                    Intent intent) {
        try {
            super.onActivityResult(requestCode, resultCode, intent);
            // 用户没有进行有效的设置操作,返回
            if (resultCode == RESULT_CANCELED) {
                return;
            }
            switch (requestCode) {
                case Util.CODE_GALLERY_REQUEST:
                    //从相册中选择图片
                    if (intent != null) {
                        cropRawPhoto(intent.getData());
                    }
                    break;

                case Util.CODE_CAMERA_REQUEST:
                    //拍照完成时
                    if (hasSdcard() && IMAGE_FILE_NAME != null) {
                        File outFile = new File(file, IMAGE_FILE_NAME);
                        Uri photoUri = null;
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                            photoUri = FileProvider.getUriForFile(CenterOfUserActivity.this, getPackageName() + ".fileprovider", outFile);
                        } else {
                            photoUri = Uri.fromFile(outFile);
                        }
                        cropRawPhoto(photoUri);
                    } else {
                        Util.showToast(CenterOfUserActivity.this, getResources().getString(R.string.no_sdcard));
                    }

                    break;

                case Util.CODE_RESULT_REQUEST:
                    if (intent != null) {
                       //裁剪完成之后通过intent.getDataString();获取图片Uri地址进行下一步操作(一般是使用七牛云上传本地图片).
//本文中将裁剪后的图片的Uri写成全局变量,可以直接获取到地址。即是下文代码中的imageUri 。
                    }

                    break;
            }
        } catch (Exception e) {
            LogUtil.e(
                    getClass(),
                    "onActivityResult(int requestCode, int resultCode,Intent intent)",
                    e);
        }
    }

其中从相册中选取照片和拍照完成之后都要进行裁剪,要说明的是在相册返回中,应该是通过intent.getData()获取在相册中选择的照片的Uri,不用进行其它的操作,将Uri传递给要裁剪的方法即可,裁剪代码如下:

/**
     * 裁剪原始的图片
     */
    public void cropRawPhoto(Uri uri) {
        try {
            File outFile = new File(file, System.currentTimeMillis() + ".jpg");
            Intent intent = new Intent("com.android.camera.action.CROP");
            Uri photoUri = null;
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
                intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
                intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
                photoUri = Uri.fromFile(outFile);
                intent.setDataAndType(uri, "image/*");// 剪切特定的图片
            } else {
                photoUri = Uri.fromFile(outFile);
                intent.setDataAndType(uri, "image/*");
            }
            imageUri = photoUri;
            // 设置裁剪
            intent.putExtra("crop", "true");

            // aspectX , aspectY :宽高的比例
            intent.putExtra("aspectX", 1);
            intent.putExtra("aspectY", 1);

            // outputX , outputY : 裁剪图片宽高
            intent.putExtra("outputX", 480);
            intent.putExtra("outputY", 480);
            // return-data为true时,会直接返回bitmap数据,但是大图裁剪时会出现问题
            // return-data为false时,不会返回bitmap,但需要指定一个MediaStore.EXTRA_OUTPUT保存图片uri
            intent.putExtra("return-data", false);// 是否返回数据
            intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);// 图像保存的路径
            intent.putExtra("outputFormat",
                    Bitmap.CompressFormat.JPEG.toString());// 返回的格式
            intent.putExtra("noFaceDetection", true);// 是否去除面部检测,
            // 如果你需要特定的比例去裁剪图片,那么这个一定要去掉,因为它会破坏掉特定的比例。
            startActivityForResult(intent, Util.CODE_RESULT_REQUEST);
        } catch (Exception e) {
            LogUtil.e(getClass(), "cropRawPhoto(Uri uri)", e);
        }
    }

其中注意一点的是在裁剪中是通过Uri.fromFile()获取对应的Uri 的,添加if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)判断只是为了动态获取读写文件的权限。在读取相册和拍照时才通过FileProvider.getUriForFile()获取Uri,这一点要分清。

猜你喜欢

转载自my.oschina.net/u/2438447/blog/1501268