1. 程式人生 > >Android相機不使用SurfaceView預覽,使用ImageView預覽

Android相機不使用SurfaceView預覽,使用ImageView預覽

剛接觸Android三個月,以下內容可能存在錯誤,歡迎指正。

以下在Android5. 1 ,API22機器下除錯的。

之前參考網上的資源,很多使用的surfaceView控制元件預覽相機,所以就用的是surfaceview。但是我的需求不僅是預覽,還需要經常性的拍照,然後在程式碼裡處理圖片,而takePicture期間有一段的卡頓時間(拍照的過程)。

後來,硬體工程師讓做一個關於雙攝的app。要求是能同時在四個地方預覽同一個攝像頭,並能切換兩個攝像頭像這樣(1代表第一個攝像頭,2代表第二個攝像頭,畫面填充了ImageView控制元件,有拉伸現象)


發現提到了一個Camera的介面: Camera.PreviewCallback。說明是這樣的: Camera.PreviewCallback Called as preview frames are displayed. This callback is invoked on the event thread open(int) was called from.

接口裡面有一個回撥方法:onPreviewFrame(byte[] data, Camera camera),在預覽幀顯示的時候呼叫。

然後在這個方法裡實現對拍到的內容data轉換成bitmap就行了。然後把這個bitmap顯示在需要的ImageView上就行了。目測來看效果還闊以。具體原始碼角度我沒有分析(還沒有那個水平),待後期分析。

注意:不能把data簡單的轉換成btmap,否則得到的bitmap為null。這是因為data格式是YUV的,需要進行資料轉換。

下面的程式碼是另起一個類,實現PreviewCallback介面:只需要看這部分程式碼就行

public class CameraPreviewCallback implements Camera.PreviewCallback {
    private ImageView imageView;

    public CameraPreviewCallback(ImageView imageView) {
        this.imageView = imageView;
    }

    @Override
    public void onPreviewFrame(byte[] data, Camera camera) {
        //格式的轉換
        Camera.Size previewSize = camera.getParameters().getPreviewSize();
        YuvImage image = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        image.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 80, stream);
        Constant.FACE_BITMAP = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());

        //在指定的imageView上顯示預覽的圖片
        imageView.setImageBitmap(Constant.FACE_BITMAP);
     
    }
}

MainActivity程式碼:主要是不需要寫surfaceHolder等,只設置了下預覽回撥。

(相機預覽只是實現了。但相機的正確處理還沒有完全弄明白,可能寫的有很多不足的地方)

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView center = null;

    private ImageView right;
    private ImageView left;
    private ImageView up;
    private ImageView down;

    // byte[] buffer=new byte[];

    private Boolean isPause = false;
    private Boolean isSwitch = false;


    private Camera mCamera1;
    private Camera mCamera2;

    private TextView tip_left; //切換攝像頭
    private TextView tip_right; //暫停/開始預覽

    private long firstTime = 0;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);
        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
        setContentView(R.layout.activity_main);

        findViews();
        setListener();
        init();
        //initSurface();
        Log.e("*************", "onCreate呼叫了");
    }


    @Override
    protected void onStart() {
        Log.e("*************", "onStart呼叫了");
        super.onStart();
    }


    private void setListener() {
        tip_left.setOnClickListener(this);
        tip_right.setOnClickListener(this);
    }

    private void findViews() {
        center = findViewById(R.id.center);


        left = findViewById(R.id.left);
        up = findViewById(R.id.up);
        down = findViewById(R.id.down);
        right = findViewById(R.id.right);

        tip_left = findViewById(R.id.tip_left);
        tip_right = findViewById(R.id.tip_right);
    }

    //拍照出錯的回撥
    Camera.ErrorCallback callback = new Camera.ErrorCallback() {
        @Override
        public void onError(int i, Camera camera) {
            switch (i) {
                case Camera.CAMERA_ERROR_SERVER_DIED:
                    Log.d("SERVER_DIED", "CAMERA_ERROR_SERVER_DIED");
                    break;
                case Camera.CAMERA_ERROR_UNKNOWN:
                    Log.d("UNKNOWN", "CAMERA_ERROR_UNKNOWN");
                    break;
            }
        }
    };

    private void init() {
        initCamera();  //開啟攝像頭
        mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));  
        mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
        mCamera1.setErrorCallback(callback);
        mCamera2.setErrorCallback(callback);
    //設定第一次開啟app時,中間的ImageView預覽攝像頭1的內容

    }



    //初始化攝像頭
    private void initCamera() {
        //如果存在攝像頭
        if (checkCameraHardware(getApplicationContext())) {
            //獲取攝像頭(首選前置,無前置選後置)
            try {
                if (openFacingFrontCamera()) {
                    Log.e("Demo", "openCameraSuccess");
                } else {
                    Log.e("Demo", "openCameraFailed");
                }
            } catch (Exception e) {
                Log.e("Demo", "autoFocus/openFacingFrontCamera函式異常:");
            }
        }
    }
//也有用open(id)開啟攝像頭的,這裡只是參考下。
    //得到後置攝像頭
    private boolean openFacingFrontCamera() {
        //嘗試開啟前置攝像頭,因為我用的是兩個後攝。下面這個for其實沒有執行
        Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
        for (int camIdx = 0, cameraCount = Camera.getNumberOfCameras(); camIdx < cameraCount; camIdx++) {
            Camera.getCameraInfo(camIdx, cameraInfo);
            if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
                try {
                    if (camIdx == 0)
                        mCamera1 = Camera.open(camIdx);
                    //這兩個攝像頭是後置攝像頭
                    if (camIdx == 1)
                        mCamera2 = Camera.open(camIdx);
                } catch (RuntimeException e) {
                    return false;
                }
            }
        }

        //如果開啟前置失敗(無前置)則開啟後置
        if (mCamera1 == null) {
            for (int camIdx = 0, cameraCount = Camera.getNumberOfCameras(); camIdx < cameraCount; camIdx++) {
                Camera.getCameraInfo(camIdx, cameraInfo);
                if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
                    try {
                        if (camIdx == 0)
                            mCamera1 = Camera.open(camIdx);
                        //這兩個攝像頭是後置攝像頭
                        if (camIdx == 1)
                            mCamera2 = Camera.open(camIdx);
                    } catch (RuntimeException e) {
                        return false;
                    }
                }
            }
        }
        try {
            mCamera1.startPreview();
            mCamera2.startPreview();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return true;
    }

    //判斷是否存在攝像頭
    private boolean checkCameraHardware(Context context) {
        if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
            // 裝置存在攝像頭
            return true;
        } else {
            // 裝置不存在攝像頭
            Toast.makeText(context, "未檢測到攝像頭", Toast.LENGTH_SHORT).show();
            return false;
        }
    }

    //對焦並拍照
    private void autoFocus() {
        mCamera1.startPreview();
        //自動對焦
        mCamera1.autoFocus(myAutoFocus);
        mCamera2.startPreview();
        //自動對焦
        mCamera2.autoFocus(myAutoFocus);
    }

    //自動對焦回撥函式(空)
    private Camera.AutoFocusCallback myAutoFocus = new Camera.AutoFocusCallback() {
        @Override
        public void onAutoFocus(boolean success, Camera camera) {
        }
    };

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
            case R.id.tip_left:  //控制預覽畫面展示到不同的控制元件上
                mCamera1.startPreview();
                mCamera2.startPreview();
                if (!isSwitch) { //isSwitch==false   camera1->中間
                    mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
                    mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
                } else {  //isSwitch==true    camera1->四周
                    mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
                    mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
                }


                isSwitch = !isSwitch;

                //以下if作用是解決:當處於暫停狀態時,點選切換畫面時,
                if (isPause) {  //本身畫面當處於暫停狀態時
                    tip_right.setText("點選暫停預覽");
                }
                isPause = !isPause;
                break;

            case R.id.tip_right:  //控制預覽暫停/開始
                if (!isPause) {
                    tip_right.setText("點選開始預覽");
                    mCamera1.stopPreview();
                    mCamera2.stopPreview();
                } else {
                    if (isSwitch) {
                        mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
                        mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
                    } else {
                        mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
                        mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
                    }
                    tip_right.setText("點選暫停預覽");
                    mCamera1.startPreview();
                    mCamera2.startPreview();
                }
                isPause = !isPause;
                break;
        }
    }


    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
            if (System.currentTimeMillis() - firstTime > 2000) {
                Toast.makeText(this, "再按一次退出程式", Toast.LENGTH_SHORT).show();
                firstTime = System.currentTimeMillis();
                return true;
            } else {
                if (mCamera1 != null) {
                    mCamera1.stopPreview();
                    mCamera1.unlock();
                    mCamera1.release();
                    mCamera1 = null;
                }
                if (mCamera2 != null) {
                    mCamera2.stopPreview();
                    mCamera2.unlock();
                    mCamera2.release();
                    mCamera2 = null;
                }
                //finish();
                System.exit(0);
            }
            return true;
        }
        return super.onKeyDown(keyCode, event);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (mCamera1 != null) {
            mCamera1.stopPreview();
            mCamera1.unlock();
            mCamera1.release();
            mCamera1 = null;
        }
        if (mCamera2 != null) {
            mCamera2.stopPreview();
            mCamera2.unlock();
            mCamera2.release();
            mCamera2 = null;
        }
    }
}