Android7.0拍照,讀取相冊,裁剪問題

首先看一下連接:Android7.0 完美適配——FileProvider 拍照裁剪全解析,這裏面有關問題講的很詳細。 總之一句話在Android7.0以前拍照,讀取相冊,裁剪是能夠經過file://Uri 來傳遞意圖,以後便不被容許。解決辦法是使用FileProvider操做。可是我按照文中所講在小米7.0的手機和小米6.0的手機中不能徹底適配。折騰好久以後才找到解決辦法。以下: 首先在AndroidManifest中添加以下代碼:android

<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";必定要對應好,否則在以後會因路徑不對致使出錯。 再以後即是在代碼中添加拍照,讀取相冊,裁剪的邏輯,以下:app

// 建立文件夾
            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();
            }
        }
    }

以上代碼只是判斷是否有權限, 第二步即是發起讀取相冊和拍照的代碼:ide

//讀取相冊
    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傳遞給要裁剪的方法便可,裁剪代碼以下:ui

/**
     * 裁剪原始的圖片
     */
    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,這一點要分清。this

相關文章
相關標籤/搜索