Android 7.0及以上調(diào)用系統(tǒng)相機(jī)拍照并返回照片

調(diào)起系統(tǒng)相機(jī)拍照

??我們在Android低版本上調(diào)用系統(tǒng)相機(jī)只需要簡單的幾行代碼就可以搞定不同。

Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri imageUri = Uri.fromFile(mediaFile);
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
startActivityForResult(openCameraIntent, TAKE_PICTURE);

??但是,自從Android7.0及以上溶耘,為了增加手機(jī)文件的安全性二拐,google大佬將這種簡單的訪問文件和拍照的方法廢棄掉了,如果繼續(xù)使用會使程序崩潰凳兵。那么在Android 7.0及以上該怎么調(diào)用系統(tǒng)相機(jī)和訪問系統(tǒng)文件呢百新?

??在Android 6.0開始,有些危險權(quán)限我們必須動態(tài)申請留荔,以前只在AndroidManifest.xml中聲明權(quán)限的方式不再適用于這些危險權(quán)限吟孙。動態(tài)獲取權(quán)限的方法不作為本文相接的內(nèi)容,如有不懂聚蝶,請移步動態(tài)申請權(quán)限這篇文章杰妓。
??我要說的是,既然你要調(diào)用相機(jī)拍照碘勉,還要訪問文件巷挥,那么就免不了動態(tài)申請權(quán)限

那么第一步:動態(tài)申請權(quán)限

Manifest.permission.WRITE_EXTERNAL_STORAGE
Manifest.permission.READ_EXTERNAL_STORAGE
Manifest.permission.CAMERA

為什么我們要用到WRITE_EXTERNAL_STORAGE呢验靡?原因是我們拍完照之后需要將照片保存到手機(jī)里面倍宾。

權(quán)限申請完后雏节,我們就開始正題吧。

Android 7.0之后高职,我們需要用content://uri來代替file://uri钩乍,所以需要用ContentProvider去訪問文件,FileProvider是很好的選擇怔锌。

要使用FileProvider寥粹,按照下面步驟繼續(xù)吧~~

第二步:配置清單文件

<provider
    android:name="android.support.v4.content.FileProvider"
    android:authorities="police.com.bsl.mobilepolice.provider"
    android:exported="false"
    android:grantUriPermissions="true">
    <meta-data
        android:name="android.support.FILE_PROVIDER_PATHS"
        android:resource="@xml/filepaths" />
</provider>

屬性介紹:
\color{blue}{android:authorities}:值一般是"項(xiàng)目的包名 + .provider"。當(dāng)我們使用FileProvider的getUriForFile方法時參數(shù)需和 清單文件注冊時的保持一致埃元。
\color{blue}{android:exported}:是否對外開放涝涤,除非是對第三方提供服務(wù),否則一般為false岛杀。
\color{blue}{android:grantUriPermissions}:是否授予臨時權(quán)限阔拳,設(shè)置為true。
\color{blue}{<meta-data />}標(biāo)簽里面是用來指定共享的路徑类嗤。
\color{blue}{android:resource="@xml/filepaths"}就是我們的共享路徑配置的xml文件糊肠,可以自己命名。該文件放在res/xml文件夾下遗锣,若沒有xml文件夾罪针,自己創(chuàng)建一個。文件取名為filepaths.xml黄伊。

<!-- filepaths.xml -->
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
    <external-path
        name="temp"
        path="Pictures" />
</paths>

<paths>內(nèi)部標(biāo)簽介紹:

\color{blue}{<external-path>}可被替換成<external-files-path>泪酱、<external-cache-path>、<file-path>还最、<cache-path>等墓阀。下面給出五個的區(qū)別:

<external-path>:共享外部存儲卡,對應(yīng)/storage/emulated/0目錄拓轻,即Environment..getExternalStorageDirectory()
<external-files-path>:共享外部存儲的文件目錄斯撮,對應(yīng)/storage/emulated/0/Android/data/包名/files,即Context.getExternalFilesDir()
<external-cache-path>:共享外部存儲的緩存目錄扶叉,對應(yīng)/storage/emulated/0/Android/data/包名/cache勿锅,即Context.getExternalCacheDir()
<file-path>:共享內(nèi)部文件存儲目錄,對應(yīng) /data/data/包名/files目錄枣氧,即Context.getFilesDir()
<cache-path>:共享內(nèi)部緩存目錄溢十,對應(yīng) /data/data/包名/cache目錄,即Context.getCacheDir()
\color{blue}{name}:隨便定義
\color{blue}{path}:需要臨時授權(quán)訪問的路徑达吞≌懦冢可以為空,表示指定目錄下的所有文件、文件夾都可以被共享

舉例:
以上方代碼為例吞鸭,最后的物理路徑為 /storage/emulated/0/Pictures寺董。
如果將<external-path>換成<file-path>,則路徑為: /data/data/包名/files/Pictures
如果將<external-path>換成<cache-path>,則路徑為: /data/data/包名/cache/Pictures

第三步:獲取URI

    public static Uri getOutputMediaFileUri(Context context) {
        File mediaFile = null;
        String cameraPath;
        try {
            File mediaStorageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath());
            if (!mediaStorageDir.exists()) {
                if (!mediaStorageDir.mkdirs()) {
                    return null;
                }
            }
            mediaFile = new File(mediaStorageDir.getPath()
                    + File.separator
                    + "Pictures/temp.jpg");//注意這里需要和filepaths.xml中配置的一樣
            cameraPath = mediaFile.getAbsolutePath();

        } catch (Exception e) {
            e.printStackTrace();
        }
      
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {// sdk >= 24  android7.0以上
            Uri contentUri = FileProvider.getUriForFile(context,
                    context.getApplicationContext().getPackageName() + ".provider",//與清單文件中android:authorities的值保持一致
                    mediaFile);//FileProvider方式或者ContentProvider。也可使用VmPolicy方式
            return contentUri;

        } else {
            return Uri.fromFile(mediaFile);//或者 Uri.isPaise("file://"+file.toString()

        }
    }

第四步:調(diào)起相機(jī)

//打開照相機(jī)
Intent openCameraIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
Uri imageUri = CameraUtil.getOutputMediaFileUri(context);
openCameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);

//Android7.0添加臨時權(quán)限標(biāo)記刻剥,此步千萬別忘了
openCameraIntent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
startActivityForResult(openCameraIntent, TAKE_PICTURE);

到此遮咖,相機(jī)已經(jīng)被調(diào)起來了。

讀取拍攝的照片

接下來拍完照我們需要拿到照片造虏,現(xiàn)在的手機(jī)像素很高盯滚,一張照片動不動就能達(dá)到幾兆大小,如果直接將如此大的照片直接放進(jìn)來酗电,很可能會造成OOM。于是我們不得不對原照片進(jìn)行壓縮内列,將縮略圖放進(jìn)來撵术。

我們獲取bitmap只需要調(diào)用下面的方法即可,傳入的是你調(diào)起相機(jī)時用的uri话瞧。

    public static Bitmap getBitmapFormUri(Context context, Uri uri) throws FileNotFoundException, IOException {
        InputStream input = context.getContentResolver().openInputStream(uri);

        //這一段代碼是不加載文件到內(nèi)存中也得到bitmap的真是寬高嫩与,主要是設(shè)置inJustDecodeBounds為true
        BitmapFactory.Options onlyBoundsOptions = new BitmapFactory.Options();
        onlyBoundsOptions.inJustDecodeBounds = true;//不加載到內(nèi)存
        onlyBoundsOptions.inDither = true;//optional
        onlyBoundsOptions.inPreferredConfig = Bitmap.Config.RGB_565;//optional
        BitmapFactory.decodeStream(input, null, onlyBoundsOptions);
        input.close();
        int originalWidth = onlyBoundsOptions.outWidth;
        int originalHeight = onlyBoundsOptions.outHeight;
        if ((originalWidth == -1) || (originalHeight == -1))
            return null;

        //圖片分辨率以480x800為標(biāo)準(zhǔn)
        float hh = 800f;//這里設(shè)置高度為800f
        float ww = 480f;//這里設(shè)置寬度為480f
        //縮放比,由于是固定比例縮放交排,只用高或者寬其中一個數(shù)據(jù)進(jìn)行計(jì)算即可
        int be = 1;//be=1表示不縮放
        if (originalWidth > originalHeight && originalWidth > ww) {//如果寬度大的話根據(jù)寬度固定大小縮放
            be = (int) (originalWidth / ww);
        } else if (originalWidth < originalHeight && originalHeight > hh) {//如果高度高的話根據(jù)寬度固定大小縮放
            be = (int) (originalHeight / hh);
        }
        if (be <= 0)
            be = 1;
        //比例壓縮
        BitmapFactory.Options bitmapOptions = new BitmapFactory.Options();
        bitmapOptions.inSampleSize = be;//設(shè)置縮放比例
        bitmapOptions.inDither = true;
        bitmapOptions.inPreferredConfig = Bitmap.Config.RGB_565;
        input = context.getContentResolver().openInputStream(uri);
        Bitmap bitmap = BitmapFactory.decodeStream(input, null, bitmapOptions);
        input.close();

        return compressImage(bitmap);//再進(jìn)行質(zhì)量壓縮
    }

上面的方法中用到了compressImage方法對bitmap進(jìn)行壓縮划滋。

   public static Bitmap compressImage(Bitmap image) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        image.compress(Bitmap.CompressFormat.JPEG, 100, baos);//質(zhì)量壓縮方法,這里100表示不壓縮埃篓,把壓縮后的數(shù)據(jù)存放到baos中
        int options = 100;
        while (baos.toByteArray().length / 1024 > 100) {  //循環(huán)判斷如果壓縮后圖片是否大于100kb,大于繼續(xù)壓縮
            baos.reset();//重置baos即清空baos
            //第一個參數(shù) :圖片格式 处坪,第二個參數(shù): 圖片質(zhì)量,100為最高架专,0為最差  同窘,第三個參數(shù):保存壓縮后的數(shù)據(jù)的流
            image.compress(Bitmap.CompressFormat.JPEG, options, baos);//這里壓縮options,把壓縮后的數(shù)據(jù)存放到baos中
            options -= 10;//每次都減少10
            if (options <= 0)
                break;
        }
        ByteArrayInputStream isBm = new ByteArrayInputStream(baos.toByteArray());//把壓縮后的數(shù)據(jù)baos存放到ByteArrayInputStream中
        Bitmap bitmap = BitmapFactory.decodeStream(isBm, null, null);//把ByteArrayInputStream數(shù)據(jù)生成圖片
        return bitmap;
    }

于是乎部脚,你的應(yīng)用可以很好地顯示拍攝的照片啦O氚睢!委刘!

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末丧没,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子锡移,更是在濱河造成了極大的恐慌呕童,老刑警劉巖,帶你破解...
    沈念sama閱讀 211,639評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件淆珊,死亡現(xiàn)場離奇詭異拉庵,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評論 3 385
  • 文/潘曉璐 我一進(jìn)店門钞支,熙熙樓的掌柜王于貴愁眉苦臉地迎上來茫蛹,“玉大人,你說我怎么就攤上這事烁挟∮ね荩” “怎么了?”我有些...
    開封第一講書人閱讀 157,221評論 0 348
  • 文/不壞的土叔 我叫張陵撼嗓,是天一觀的道長柬采。 經(jīng)常有香客問我,道長且警,這世上最難降的妖魔是什么粉捻? 我笑而不...
    開封第一講書人閱讀 56,474評論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮斑芜,結(jié)果婚禮上肩刃,老公的妹妹穿的比我還像新娘。我一直安慰自己杏头,他們只是感情好盈包,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評論 6 386
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著醇王,像睡著了一般呢燥。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上寓娩,一...
    開封第一講書人閱讀 49,816評論 1 290
  • 那天叛氨,我揣著相機(jī)與錄音,去河邊找鬼棘伴。 笑死力试,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的排嫌。 我是一名探鬼主播畸裳,決...
    沈念sama閱讀 38,957評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼淳地!你這毒婦竟也來了怖糊?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,718評論 0 266
  • 序言:老撾萬榮一對情侶失蹤颇象,失蹤者是張志新(化名)和其女友劉穎伍伤,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體遣钳,經(jīng)...
    沈念sama閱讀 44,176評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡扰魂,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片劝评。...
    茶點(diǎn)故事閱讀 38,646評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡姐直,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出蒋畜,到底是詐尸還是另有隱情声畏,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評論 4 330
  • 正文 年R本政府宣布姻成,位于F島的核電站插龄,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏科展。R本人自食惡果不足惜均牢,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望才睹。 院中可真熱鬧徘跪,春花似錦、人聲如沸砂竖。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,755評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽乎澄。三九已至,卻和暖如春测摔,著一層夾襖步出監(jiān)牢的瞬間置济,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,987評論 1 266
  • 我被黑心中介騙來泰國打工锋八, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留浙于,地道東北人。 一個月前我還...
    沈念sama閱讀 46,358評論 2 360
  • 正文 我出身青樓挟纱,卻偏偏與公主長得像羞酗,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子紊服,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評論 2 348

推薦閱讀更多精彩內(nèi)容