Android中的拍照camera和camera2

在android我們可以調用系統的相機來進行照相,當然也可以設計自己的照相頁面,通過surface進行預覽,通過camera api來進行拍照即可。

在Android 5.0之後google推薦使用的Camera2 Api,但是由於兼容問題我們大部分還在使用Camera Api。這裏我們一起簡單介紹一下,僅僅是預覽拍照,至於細節的設置,比如圖像大小比例、對焦、圖像旋轉等就不一一細說了。

首先我們需要在佈局中加入一個SurfaceView

Camera Api

    var camera: Camera? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //爲surfaceholder添加監聽
        surface.holder.addCallback(object : SurfaceHolder.Callback{
            override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {

            }

            override fun surfaceDestroyed(p0: SurfaceHolder?) {
            }

            override fun surfaceCreated(p0: SurfaceHolder?) {
                //當surface創建完成初始化相機
                initCamera1()
            }

        })

        take.setOnClickListener {
            //拍照
            takePicture1()
        }
    }

    fun initCamera1(){
        //打開相機,開啓預覽
        camera = Camera.open()
        camera?.setPreviewDisplay(surface.holder)
        camera?.startPreview()
    }

    fun takePicture1(){
        //拍照,通過回調獲取圖片數據
        camera?.takePicture({},
                {bytes, camera ->
                    if(bytes == null){
                        return@takePicture
                    }
                    var b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                    imageView.setImageBitmap(b)
                },
                {bytes, camera ->
                    if(bytes == null){
                        return@takePicture
                    }
                    var b = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                    imageView2.setImageBitmap(b)
                })
    }

可以看到使用Camera比較簡單,打開相機開啓預覽,拍照在回調中處理即可。

Camera2 Api

Camera2的Api相比Camera要複雜很多

    var mCameraDevice : CameraDevice? = null
    var mCameraCaptureSession : CameraCaptureSession? = null
    var childHandler : Handler? = null
    @SuppressLint("NewApi")
    var imageReader : ImageReader? = null

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        //爲surfaceholder添加監聽
        surface.holder.addCallback(object : SurfaceHolder.Callback{
            override fun surfaceChanged(p0: SurfaceHolder?, p1: Int, p2: Int, p3: Int) {

            }

            override fun surfaceDestroyed(p0: SurfaceHolder?) {
            }

            override fun surfaceCreated(p0: SurfaceHolder?) {
                //當surface創建完成初始化相機
                initCamera2()
            }

        })

        take.setOnClickListener {
            //拍照
            takePicture2()
        }
    }

    fun initCamera2(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            //因爲處理圖片比較耗時,所以這裏新建一個線程的handler來處理
            var handlerThread = HandlerThread("camera")
            handlerThread.start()
            childHandler = Handler(handlerThread.looper)

            //創建一個ImageReader,後面會通過這個獲取拍照的圖片
            imageReader = ImageReader.newInstance(500, 500, ImageFormat.JPEG,2)
            //爲ImageReader添加回調,這裏使用我們新建的線程的handler處理圖片
            imageReader?.setOnImageAvailableListener(object : ImageReader.OnImageAvailableListener{
                override fun onImageAvailable(imageReader: ImageReader) {
                    //得到圖片後,關閉相機,從ImageReader中獲取圖片數據
                    mCameraDevice?.close()
                    var image = imageReader.acquireNextImage()
                    var buffer = image.planes[0].buffer
                    var bytes = ByteArray(buffer.remaining())
                    buffer.get(bytes)
                    var bitmap = BitmapFactory.decodeByteArray(bytes, 0, bytes.size)
                    //由於這部分是在子線程中處理的,所以對ui操作要放到主線程中
                    runOnUiThread {
                        imageView.setImageBitmap(bitmap)
                    }
                }

            }, childHandler)

            //通過CameraManager打開相機,注意這裏的handler是主線程的。
            var cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
            cameraManager.openCamera(CameraCharacteristics.LENS_FACING_FRONT.toString(), object : CameraDevice.StateCallback() {
                override fun onOpened(cameraDevice: CameraDevice) {
                    //相機打開後,得到CameraDevice並開啓預覽
                    mCameraDevice = cameraDevice
                    camera2Preview()
                }

                override fun onDisconnected(p0: CameraDevice) {
                }

                override fun onError(p0: CameraDevice, p1: Int) {
                }

            }, Handler(Looper.getMainLooper()))
        }
    }

    fun camera2Preview(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mCameraDevice?.let {
                //新建一個PRIVIEW的CaptureRequest,將surface添加進去
                var builder = it.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
                builder.addTarget(surface.holder.surface)
                //通過CameraDevice新建一個CaptureSession事務,在回調中開啓預覽
                it.createCaptureSession(Arrays.asList(surface.holder.surface, imageReader?.surface), object : CameraCaptureSession.StateCallback() {
                    override fun onConfigureFailed(p0: CameraCaptureSession) {
                    }

                    override fun onConfigured(cameraCaptureSession: CameraCaptureSession) {
                        //獲得對應的CaptureSession事務,並設置預覽的request開始預覽
                        mCameraCaptureSession = cameraCaptureSession
                        var previewRequest = builder.build()
                        mCameraCaptureSession?.setRepeatingRequest(previewRequest, null ,childHandler)
                    }

                }, childHandler)
            }
        }
    }

    //拍照
    fun takePicture2(){
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
            mCameraDevice?.let {
                //新建一個拍照請求,並添加之前創建的ImageReader的surface
                var builder = it.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE)
                builder.addTarget(imageReader!!.surface)
                var previewRequest = builder.build()
                //通過預覽是獲取的CaptureSession事務,執行拍照請求,結果會通過之前設置的ImageReader的回調返回
                mCameraCaptureSession?.setRepeatingRequest(previewRequest, null ,childHandler)
            }
        }
    }

使用Camera2 Api的流程基本與Camera一樣,但是代碼複雜很多,主要是需要通過事務CaptureSession來處理預覽及拍照,而且爲了處理圖片還需要一個新建子線程,同時拍照結果也是通過ImageReader封裝起來的。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章