Android 自定義Camera之SurfaceView的使用(6.0權(quán)限申請(qǐng))

序言

由于前段時(shí)間在準(zhǔn)備跳槽琼梆,所以一直沒有更新。不過窿吩,從這個(gè)月開始,我會(huì)繼續(xù)開始記錄自己在android開發(fā)中遇到的一些坑错览,或?qū)懸恍┍容^有意思的文章纫雁。希望大家繼續(xù)關(guān)注。好了倾哺,開始切入正題轧邪。


概述

這段時(shí)間開始接觸到Camera相關(guān)的東西,所以就打算自己寫一個(gè)小demo來熟悉一下流程和要點(diǎn)羞海。當(dāng)然忌愚,本文使用SurfaceView來實(shí)現(xiàn)一個(gè)Camera,同時(shí)適配6.0權(quán)限(開始沒6.0動(dòng)態(tài)權(quán)限却邓,后來因?yàn)樯磉吅芏喽际?.0硕糊,所以簡(jiǎn)單的做了一下6.0權(quán)限),以及sd卡的讀寫,圖片顯示不全等一些相關(guān)的知識(shí)點(diǎn)简十。

相關(guān)知識(shí)的介紹

  • SurfaceView :使用場(chǎng)景界面迅速更新對(duì)幀率要求較高的情況檬某。SurfaceView繼承 View,SurfaceView和View最本質(zhì)的區(qū)別在于螟蝙,SurfaceView是在一個(gè)新起的單獨(dú)線程中可以重新繪制畫面恢恼,而View必須在UI的主線程中更新畫面。因本文主要講的是怎么使用胰默,所以詳細(xì)介紹可以看SurfaceView或者Google查看场斑。

  • RxPermissions Github地址:本文使用了原生的6.0權(quán)限請(qǐng)求和RxPermissions。RxPermissions是一個(gè)6.0動(dòng)態(tài)權(quán)限管理的一個(gè)library庫(kù)牵署,它的使用需要結(jié)合Rxjava一起漏隐,因?yàn)镽xPermissions返回的是一個(gè)Observable,所以如果不準(zhǔn)備使用Rxjava碟刺,可以去嘗試一下其他的library锁保。可以參考一下弘洋的6.0權(quán)限管理

  • 還有讀寫文件的基本使用方法以及一些圖片的簡(jiǎn)單處理


實(shí)現(xiàn)

一 :主要邏輯在MainActivity半沽,在onCreate的時(shí)候申請(qǐng)權(quán)限處理爽柒,在onResume的時(shí)候startPreview
開啟預(yù)覽,在onPause的時(shí)候releasePreview關(guān)閉預(yù)覽并釋放Camera(由于一直持有會(huì)出現(xiàn)oom)者填。

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        frameLayout = (FrameLayout) findViewById(R.id.activity_main);
        btn_capture = (ImageView) findViewById(R.id.btn_capture);
        btn_capture.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                capture();
            }
        });
        /**
         * 使用系統(tǒng)API請(qǐng)求相機(jī)權(quán)限
         */
        if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
            isCamera = false;
            ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, MY_PERMISSIONS_REQUEST_CAMERA);
        } else {
            isCamera = true;
            initCamera();
            initDefult();

        }
    }
    /**
     * 獲得Camera浩村,開啟預(yù)覽
     *
     */
    @Override
    protected void onResume() {
        super.onResume();
        if (isCamera == false) return;
        if (mCamera == null) {
            mCamera = getCamera();
            if (sHolder != null) {
                setStartPreview(mCamera, sHolder);
            }
        }
    }
    /**
     * 停止預(yù)覽,銷毀Camera
     */
    @Override
    protected void onPause() {
        super.onPause();
        releasePreview();
    }

二:initCamera()中主要是初始化Camera和SurfaceView占哟,并且獲得SurfaceHolder心墅,然后SurfaceHolder添加回調(diào),并調(diào)用setStartPreview榨乎,開啟預(yù)覽怎燥。

    /**
     * 初始化Camera相關(guān)
     */
    private void initCamera() {
        mCamera = getCamera();
        surface_camera = (SurfaceView) findViewById(R.id.surface_camera);
        frameLayout.bringChildToFront(surface_camera);
        frameLayout.bringChildToFront(btn_capture);
        sHolder = surface_camera.getHolder();
        sHolder.addCallback(this);
        surface_camera.setOnClickListener(this);
        setStartPreview(mCamera,sHolder);    //由于APP在第一次安裝時(shí),onResume不會(huì)執(zhí)行蜜暑,所以重新獲得cemera權(quán)限以后重新start
    }

注:大家會(huì)看到铐姚,在onCeate和onResume都調(diào)用了 mCamera = getCamera(),原因是在于肛捍,當(dāng)app第一次安裝時(shí)隐绵,系統(tǒng)會(huì)依次執(zhí)行Activity的生命周期,如果只在onResume中調(diào)用拙毫,會(huì)發(fā)現(xiàn)并沒有使用相機(jī)依许。原因是在權(quán)限申請(qǐng)時(shí),是另起了一個(gè)線程缀蹄,所以獲得Camera權(quán)限后峭跳,onResume已經(jīng)執(zhí)行完成膘婶。因此添加isCamera字段,來標(biāo)記是否已經(jīng)獲取權(quán)限坦康,同時(shí)在取得權(quán)限后竣付,調(diào)用了 mCamera = getCamera()。

三:當(dāng)添加了SurfaceHolder回調(diào)后滞欠,會(huì)重寫三個(gè)方法:surfaceCreated()古胆,surfaceChanged(),surfaceDestroyed()筛璧。分別是創(chuàng)建逸绎,變化和銷毀。

@Override
    public void surfaceCreated(SurfaceHolder holder) {
        setStartPreview(mCamera, sHolder);
    }

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        mCamera.stopPreview();
        setStartPreview(mCamera, sHolder);
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        releasePreview();
    }

四:接下來看最重要的setStartPreview()和 releasePreview()夭谤。這兩個(gè)方法中棺牧,setStartPreview中主要是做一下初始化Preview的分辨率,調(diào)整一下預(yù)覽的成像角度朗儒。releasePreview中主要是給setPreviewCallback置null颊乘,停止預(yù)覽并釋放Camera。

    /**
     * 開啟Camera預(yù)覽
     */
    private void setStartPreview(Camera camera, SurfaceHolder holder) {
        try {
            Camera.Parameters parameters = camera.getParameters();
            List<Camera.Size> size2 = parameters.getSupportedPreviewSizes();     //得到手機(jī)支持的預(yù)覽分辨率
            parameters.setPreviewSize(size2.get(0).width,size2.get(0).height);
            camera.setPreviewDisplay(holder);//綁定holder
            camera.setDisplayOrientation(getPreviewDegree(MainActivity.this));//將系統(tǒng)Camera角度進(jìn)行調(diào)整
            camera.startPreview();//開啟預(yù)覽
            camera.setParameters(parameters);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
     /**
     * 釋放Camera
     */
    private void releasePreview() {
        if (mCamera == null) return;
        mCamera.setPreviewCallback(null);
        mCamera.stopPreview();//停止預(yù)覽
        mCamera.release();
        mCamera = null;
    }

五:拍照和點(diǎn)擊屏幕實(shí)現(xiàn)對(duì)焦醉锄。點(diǎn)擊拍照前乏悄,會(huì)設(shè)置一下Picture相關(guān)的參數(shù)。當(dāng)onAutoFocus返回true時(shí)恳不,說明對(duì)焦成功檩小,然后調(diào)用Camera的takePicture實(shí)現(xiàn)拍照。

     Camera.Parameters parameters = mCamera.getParameters();
        List<Camera.Size> supportedPictureSizes = parameters.getSupportedPictureSizes();
        parameters.setPictureFormat(ImageFormat.JPEG);//設(shè)置圖片樣式
        parameters.setPictureSize(supportedPictureSizes.get(0).width, supportedPictureSizes.get(0).height);//設(shè)置圖片大小
        parameters.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);//自動(dòng)對(duì)焦
        mCamera.setParameters(parameters);
        mCamera.autoFocus(new Camera.AutoFocusCallback() {
            @Override
            public void onAutoFocus(boolean success, Camera camera) {
                if (success) {
                    mCamera.takePicture(null, null, pictureCallback);
                }
            }
        });

六:拍照成功后烟勋,使用RxPermissions申請(qǐng)寫入sd權(quán)限规求。然后完成跳轉(zhuǎn)到預(yù)覽界面。其中返回的data是一個(gè)拍照完成后卵惦,沒有壓縮過完整的圖片byte[]阻肿。

      //保存圖片
                                String absolutePath = FileUtil.createIfNotExist(path);
                                FileUtil.writeBytes(path, data);
                                Intent intent = new Intent(MainActivity.this,ImageActivity.class);
                                intent.putExtra("path",absolutePath);
                                startActivity(intent);

總結(jié)

由于本人原來并沒有涉及到相關(guān)模塊,但是在剛接觸的時(shí)候沮尿,感覺挺簡(jiǎn)單冕茅,就是按部就班的實(shí)現(xiàn)一些方法和生命周期,但是當(dāng)一步步做下來的時(shí)候蛹找,發(fā)現(xiàn)其中涉及到的細(xì)節(jié)還是挺多。比如:
1.在我要設(shè)置setPreviewSize和setPictureSize時(shí)哨坪,我發(fā)現(xiàn)很容易導(dǎo)致程序崩潰庸疾,所以調(diào)用getSupportedPictureSizes,獲取當(dāng)前支持的各種分辨率当编,然后使用最高的分辨來設(shè)置届慈。
2.由于本人沒有6.0以上的測(cè)試機(jī),所以很多問題難以定位。在添加6.0權(quán)限后金顿,發(fā)現(xiàn)原有的邏輯需要重新思考臊泌,所以花費(fèi)了一些時(shí)間和精力。
3.是大家經(jīng)常會(huì)遇到的圖片翻轉(zhuǎn)或者角度問題揍拆。
4.由于安卓機(jī)型實(shí)在太多渠概,所以還要考慮多種屏幕下的顯示和預(yù)覽問題。


源碼

源碼下載地址源碼中注釋寫的很清楚嫂拴,本文只是把關(guān)鍵代碼貼出來播揪,如有需要,歡迎大家下載筒狠。

如果大家在學(xué)習(xí)時(shí)有問題猪狈,歡迎大家隨時(shí)聯(lián)系或者留言,我看見后會(huì)第一時(shí)間回復(fù)并解決辩恼。最后雇庙,愿大家在小長(zhǎng)假中玩得開心,祝愿你我gaygayup灶伊,在編碼的路上堅(jiān)挺下去疆前。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市谁帕,隨后出現(xiàn)的幾起案子峡继,更是在濱河造成了極大的恐慌,老刑警劉巖匈挖,帶你破解...
    沈念sama閱讀 212,542評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件碾牌,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡儡循,警方通過查閱死者的電腦和手機(jī)舶吗,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,596評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來择膝,“玉大人誓琼,你說我怎么就攤上這事‰茸剑” “怎么了腹侣?”我有些...
    開封第一講書人閱讀 158,021評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)齿穗。 經(jīng)常有香客問我傲隶,道長(zhǎng),這世上最難降的妖魔是什么窃页? 我笑而不...
    開封第一講書人閱讀 56,682評(píng)論 1 284
  • 正文 為了忘掉前任跺株,我火速辦了婚禮复濒,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘乒省。我一直安慰自己巧颈,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,792評(píng)論 6 386
  • 文/花漫 我一把揭開白布袖扛。 她就那樣靜靜地躺著砸泛,像睡著了一般。 火紅的嫁衣襯著肌膚如雪攻锰。 梳的紋絲不亂的頭發(fā)上晾嘶,一...
    開封第一講書人閱讀 49,985評(píng)論 1 291
  • 那天,我揣著相機(jī)與錄音娶吞,去河邊找鬼垒迂。 笑死,一個(gè)胖子當(dāng)著我的面吹牛妒蛇,可吹牛的內(nèi)容都是我干的机断。 我是一名探鬼主播,決...
    沈念sama閱讀 39,107評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼绣夺,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼吏奸!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起陶耍,我...
    開封第一講書人閱讀 37,845評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤奋蔚,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后烈钞,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體泊碑,經(jīng)...
    沈念sama閱讀 44,299評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,612評(píng)論 2 327
  • 正文 我和宋清朗相戀三年毯欣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了馒过。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,747評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡酗钞,死狀恐怖腹忽,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情砚作,我是刑警寧澤窘奏,帶...
    沈念sama閱讀 34,441評(píng)論 4 333
  • 正文 年R本政府宣布,位于F島的核電站葫录,受9級(jí)特大地震影響蔼夜,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜压昼,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,072評(píng)論 3 317
  • 文/蒙蒙 一求冷、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧窍霞,春花似錦匠题、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,828評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至冷溃,卻和暖如春钱磅,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背似枕。 一陣腳步聲響...
    開封第一講書人閱讀 32,069評(píng)論 1 267
  • 我被黑心中介騙來泰國(guó)打工盖淡, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人凿歼。 一個(gè)月前我還...
    沈念sama閱讀 46,545評(píng)論 2 362
  • 正文 我出身青樓褪迟,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親答憔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子味赃,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,658評(píng)論 2 350

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