Android7.0以上調(diào)用相機(jī)拍照并返回圖片和從相冊(cè)中選擇相片

  • 自Android 6.0以后對(duì)某些涉及用戶隱私權(quán)限的獲取需要?jiǎng)討B(tài)獲取,所以首先是檢查權(quán)限,如沒(méi)有權(quán)限則動(dòng)態(tài)申請(qǐng)權(quán)限,這里我們需要用到的權(quán)限是WRITE_EXTERNAL_STORAGE和CAMERA。
  • 自Android 7.0后系統(tǒng)禁止應(yīng)用向外部公開(kāi)file://URI ,因此需要FileProvider來(lái)向外界傳遞URI。
  • 獲取到拍照后的照片,按照現(xiàn)在的手機(jī)拍照文件大小來(lái)說(shuō)不做處理直接展示很容易發(fā)生OOM,可以通過(guò)采樣率對(duì)圖片進(jìn)行縮小,或者壓縮的操作。

接下來(lái)我們看如何實(shí)現(xiàn)調(diào)用相機(jī)拍照并返回圖片和從相冊(cè)中選擇相
片。

一、調(diào)用相機(jī)拍照并返回圖片

1.動(dòng)態(tài)申請(qǐng)權(quán)限

首先在Mainfest.xml文件中聲明權(quán)限

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
    <uses-permission android:name="android.permission.CAMERA"/> 

在代碼中動(dòng)態(tài)申請(qǐng)

  private void requestPermission() {
        int permission = ActivityCompat.checkSelfPermission(this, 
                Manifest.permission.WRITE_EXTERNAL_STORAGE);
        if (permission != PackageManager.PERMISSION_GRANTED) {
            //動(dòng)態(tài)申請(qǐng)權(quán)限
            ActivityCompat.requestPermissions(
                    this,
                    PERMISSIONS_STORAGE,
                    REQUEST_EXTERNAL_STORAGE
            );
        }
        //拍照的權(quán)限
        if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) 
                != PackageManager.PERMISSION_GRANTED) {
            ActivityCompat.requestPermissions(this, 
                    new String[]{Manifest.permission.CAMERA}, REQUEST_EXTERNAL_STORAGE);

        }
    }

我們可以重寫(xiě)onRequestPermissionsResult的方法對(duì)是否申請(qǐng)權(quán)限進(jìn)行處理。

@Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        for (int result : grantResults) {
            if (result != PackageManager.PERMISSION_GRANTED) {
                this.finish();
            }
        }
    }

2.FileProvider

FileProvider是是ContentProvider的一個(gè)子類,用于應(yīng)用程序之間私有文件的傳遞。自Android 7.0后系統(tǒng)禁止應(yīng)用向外部公開(kāi)file://URI ,因此需要FileProvider來(lái)向外界傳遞URI,傳遞的形式是content : //Uri,使用時(shí)需要在清單文件中注冊(cè)。

    <provider
            android:authorities="com.hx.yolov5.provider"
            android:name="androidx.core.content.FileProvider"
            android:grantUriPermissions="true"
            android:exported="false"><!-- 一定要加這句-->
            <meta-data
                android:name="android.support.FILE_PROVIDER_PATHS"
                android:resource="@xml/filepath"/>
        </provider>

上面代碼中的是我們?cè)趓es目錄下新建的一個(gè)xml文件夾,在文件夾下創(chuàng)建一個(gè)名filepath的xml文件

<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <!--files-path  相當(dāng)于 getFilesDir()-->
    <files-path name="my_images" path="images"/>
    <!--cache-path  相當(dāng)于 getCacheDir()-->
    <!--external-path  相當(dāng)于 Environment.getExternalStorageDirectory()-->
    <!--external-files-path  相當(dāng)于 getExternalFilesDir("") -->
    <!--external-cache-path  相當(dāng)于 getExternalCacheDir() -->

</paths>

3. 獲取URI

通過(guò)FileProvider.getUriForFile來(lái)獲取,其中com.hx.yolov5.provider就是在注冊(cè)文件的provider中的android:authorities的值

  /**
     * 獲取uri
     */
    public Uri getMediaFileUri(Context context) {
        String cameraPath = getFilesDir() + File.separator + "images" + File.separator;
        mediaFile = new File(cameraPath, "picture" + System.currentTimeMillis() + ".jpg");
        if (!mediaFile.exists()) {
            mediaFile.getParentFile().mkdirs();
        }
        //sdk>=24 android7以上
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
            imageUri = FileProvider.getUriForFile(context, "com.hx.yolov5.provider", mediaFile);

        } else {
            imageUri = Uri.fromFile(mediaFile);
        }
        return imageUri;
    }

4.調(diào)起相機(jī)

  /**
     * 打開(kāi)相機(jī)
     */
    private void IntentCamera() {
        Intent openCameraIntent = new Intent("android.media.action.IMAGE_CAPTURE");
        imageUri = getMediaFileUri(MainActivity.this);
        openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        //將存儲(chǔ)圖片的uri讀寫(xiě)權(quán)限授權(quán)給相機(jī)應(yīng)用
        //Android7.0添加臨時(shí)權(quán)限標(biāo)記,此步千萬(wàn)別忘了
        openCameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
        openCameraIntent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        startActivityForResult(openCameraIntent, CAMERA_RESULT);
    }

5.顯示圖片

重寫(xiě)onActivityResult的方法:

  @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            case REQUEST_PICK_IMAGE:
                imageBitmap = getBitmapFromUri(getRealUri(data.getData()), 400, 400);
                break;
            case CAMERA_RESULT:
                imageBitmap = getBitmapFromUri(mediaFile.getAbsolutePath(), 400, 400);
                break;
            default:
                break;
        }
        if (imageBitmap != null) {
            resultImageView.setImageBitmap(imageBitmap);
        }

    }

獲取圖片并且通過(guò)采樣率進(jìn)行壓縮

   /**
     * 通過(guò)采樣率來(lái)加載圖片
     */
    public Bitmap getBitmapFromUri(String uri, int width, int height) {
        final BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        options.inSampleSize = calculateInSampleSize(options, width, height);
        options.inJustDecodeBounds = false;
        Bitmap bitmap = BitmapFactory.decodeFile(uri, options);
        return bitmap;
    }

    /**
     * 計(jì)算合適的采樣率
     *
     * @param options
     * @param width
     * @param height
     * @return
     */
    private int calculateInSampleSize(BitmapFactory.Options options, int width, int height) {
        final int originHeight = options.outHeight;
        final int originWidth = options.outWidth;
        int inSampleSize = 1;
        if (originHeight > height || originWidth > width) {
            final int halfHeight = originHeight / 2;
            final int halfWidth = originWidth / 2;
            while ((halfHeight / inSampleSize) >= height && (halfWidth / inSampleSize) >= width) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

之前參考了網(wǎng)上的方法通過(guò) InputStream input = context.getContentResolver().openInputStream(uri);這個(gè)進(jìn)行獲取的圖片的文件流獲取失?。ń鉀Q方法還沒(méi)找到,如果知道解決方法的歡迎留言),后來(lái)發(fā)現(xiàn)可以直接通過(guò) Bitmap bitmap = BitmapFactory.decodeFile(uri, options);獲得Bitmap對(duì)象之后做相應(yīng)的處理。
對(duì)圖片進(jìn)行壓縮


    /**
     * 對(duì)圖片進(jìn)行質(zhì)量的壓縮
     * 改方法刪除,因?yàn)閴嚎s后的圖片過(guò)于模糊
     *
     * @param bitmap
     * @return
     */
    @Deprecated
    private Bitmap compressImage(Bitmap bitmap) {
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        //質(zhì)量壓縮方法,這里的100表示不壓縮,把壓縮后數(shù)據(jù)存放到outputStream
        bitmap.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);
        int options = 100;
        //是否大于100k
        while (outputStream.toByteArray().length / 1024 > 100) {
            //重置outputStream
            outputStream.reset();
            //第一個(gè)參數(shù),圖片格式,第二個(gè)參數(shù):圖片的質(zhì)量,100為最高,0為最差  ,第三個(gè)參數(shù):保存壓縮后的數(shù)據(jù)的流
            bitmap.compress(Bitmap.CompressFormat.JPEG, options, outputStream);
            options -= 10;
            if (options <= 0) {
                break;
            }
        }
        ////把壓縮后的數(shù)據(jù)存放到ByteArrayInputStream中
        ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
        //生成圖片
        Bitmap imageBitmap = BitmapFactory.decodeStream(inputStream, null, null);
        return imageBitmap;
    }

二、從相冊(cè)中提取

1.在申請(qǐng)權(quán)限的前提下,打開(kāi)相冊(cè)

 Intent intent = new Intent(Intent.ACTION_PICK);
 intent.setType("image/*");
 startActivityForResult(intent, REQUEST_PICK_IMAGE);

2.重寫(xiě)onActivityResult的方法:

  @Override
    protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        switch (requestCode) {
            //相冊(cè)選擇圖片返回的結(jié)果
            case REQUEST_PICK_IMAGE:
                imageBitmap = getBitmapFromUri(getRealUri(data.getData()), 400, 400);
                break;
            case CAMERA_RESULT:
                //拍照返回的結(jié)果
                imageBitmap = getBitmapFromUri(mediaFile.getAbsolutePath(), 400, 400);
                break;
            default:
                break;
        }
        if (imageBitmap != null) {
            resultImageView.setImageBitmap(imageBitmap);
        }

    }

因?yàn)閐ata.getData()返回的uri的形式是content://所以進(jìn)行轉(zhuǎn)化,然后步驟和上面調(diào)起相機(jī)的的第5個(gè)步驟相同

 /**
     * 獲取相冊(cè)中返回的真正的路徑
     *
     * @param selectedImage
     * @return
     */
    public String getRealUri(Uri selectedImage) {
        String[] filePathColumn = {MediaStore.Images.Media.DATA};
        Cursor cursor = this.getContentResolver().query(selectedImage, filePathColumn, null, null, null);
        assert cursor != null;
        cursor.moveToFirst();
        int columnIndex = cursor.getColumnIndex(filePathColumn[0]);
        String picturePath = cursor.getString(columnIndex);
        cursor.close();

        return picturePath;
    }

參考鏈接:
https://blog.csdn.net/CLinuxF/article/details/103069101?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.nonecase&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-4.nonecase

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容