Android使用SurfaceView+Camera實現(xiàn)無卡頓拍照(相機預覽圖像的獲取與保存)

一、前言

最近前同事兼好基友老戴問我要我之前那個可以無卡頓拍照的demo,翻了一翻我的demo項目文件夾,有點真實

demo目錄

加上程序員都是不喜歡看自己以前寫的代碼的特性,于是決定將這個功能封裝一下,方便他人當然也是方便自己

這個功能的出處還是以前我們做的刷卡考勤機,考勤的時候需要取到考勤圖片,所以需要進行拍照

我一開始只是使用常規(guī)的Camera的takePicture方法來獲取照片,但是實際應用中會出現(xiàn),拍照速度緩慢

當時我還去現(xiàn)場看了一下使用情況,負責人跟我抱怨說拍攝速度很慢,給我演示了一下,確實是有一個卡頓,當然這很好理解,我理直氣壯的跟她解釋說你用手機拍照不也是會停頓一下的嗎,手機需要聚焦啊,這個本來就是這樣的

而負責人跟我說了某某考勤機拍照沒有停頓啊,非??斓?,我第一反應是,應該是windows的機子

結果看到發(fā)現(xiàn)人家的也是android的機器,于是便陷入了沉思

我們別的我不知道,但是抄襲這一招可是鐵打的,于是乎便開始了對android相機的探索


正如我標題寫的,為了實現(xiàn)我卡頓的拍照,使用的是SurfaceView+Camera的方式,通過相機的預覽到surfaceView上,然后通過Camera的setPreviewCallback函數(shù)的回調來當前幀的圖片,便不會有任何的卡頓

二、效果圖

點擊拍照之后,可以獲取到當前幀的圖片的BitMap對象,以及保存至本地的路徑

效果圖

三、功能實現(xiàn)

(一)如何使用

首先先看一下布局文件
一個SurfaceView用來實時顯示相機的畫面
文本框和ImageView用來顯示保存圖片的路徑和顯示圖片

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:orientation="vertical"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <SurfaceView
        android:id="@+id/surfaceview"
        android:layout_width="320dp"
        android:layout_height="240dp" />

    <LinearLayout
        android:orientation="vertical"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <Button
            android:id="@+id/btn_take_photo"
            android:text="拍照"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <TextView
            android:id="@+id/tv_pic_dir"
            android:text="圖片路徑:"
            android:textSize="14sp"
            android:textColor="#000000"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

        <ImageView
            android:background="#000000"
            android:id="@+id/img_pic"
            android:layout_width="160dp"
            android:layout_height="120dp" />

    </LinearLayout>

</LinearLayout>

使用已封裝的CameraTakeManager,傳入三個參數(shù)分別為activity對象,surfaceView控件對象,一個自定義的回調

回調的兩個函數(shù)onSuccess中返回以保存的圖片和BitMap對象

onFail返回失敗信息

        manager = new CameraTakeManager(this, previewView, new CameraTakeListener() {
            @Override
            public void onSuccess(File bitmapFile, Bitmap mBitmap) {
                imgPic.setImageBitmap(mBitmap);
                tvPicDir.setText("圖片路徑:" + bitmapFile.getPath());
            }

            @Override
             public void onFail(String error) {
                LogUtil.e(error);
            }
        });

通過點擊按鈕用來獲取照片,進入CameraTakeManager的回調

    @OnClick({R.id.btn_take_photo})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.btn_take_photo:
                /** 點擊拍照獲取照片*/
                manager.takePhoto();
                break;
        }
    }

(二)實現(xiàn)的代碼

這邊自定義了一個SurfaceViewCallback類來實現(xiàn)SurfaceHolder.Callback接口

先是在surfaceChanged回調中開啟camera的預覽

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        if (previewing) {
            mCamera.stopPreview();
            previewing = false;
        }

        try {
            mCamera.setPreviewDisplay(holder);
            mCamera.startPreview();
            previewing = true;
            setCameraDisplayOrientation(activity, mCurrentCamIndex, mCamera);
        } catch (Exception e) {
        }
    }

在surfaceCreated回調中實現(xiàn)Camera的setPreviewCallback函數(shù)來獲取相機每一幀的回調

用canTake變量來判斷當前是否需要拍照,為true時,則取當前幀的圖像,生成bitmap同時壓縮一份圖片文件到本地保存,并把數(shù)據(jù)回調給接口

實現(xiàn)拍照功能

    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        if (!hasSurface) {
            hasSurface = true;
            mCamera = openFrontFacingCameraGingerbread();

            if (mCamera == null){
                listener.onFail("沒有可用的攝像頭");
                return;
            }
            mCamera.setPreviewCallback(new Camera.PreviewCallback() {
                @Override
                public void onPreviewFrame(byte[] bytes, Camera camera) {
                    if (canTake) {
                        getSurfacePic(bytes, camera);
                        canTake = false;
                    }
                }
            });
        }
    }

四、結語

到這里就算是完成了,技藝不精,希望大家多提提意見,我也會第一時間改良,記得給我點贊哦

在最后貼一下github的源碼地址https://github.com/Giftedcat/CameraTakeManager

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

友情鏈接更多精彩內容