创建多媒体APP 之 捕获图像:控制相机

这一节里面我讲述一下如何利用framework的API直接控制相机

直接去控制相机,相比于利用相机拍照或者录像,需要更多的代码。如果你想创建一个专业的相机APP或者是在你的UI里面完全的集成相机,这节里面会教你如何实现它。

创建一个相机对象

首先要获取到一个相机的实例。如android自身的相机APP里面一样,在一个独立的线程里面用打开相机的方法来得到这个Camera的实例,一般在onCreate函数里面进行。这种方式是防止这个动作会产生耗时,导致UI线程假死。在更多的实现方法中,打开相机的动作可以在onResume里面实现,方法代码的重利用和相机的一系列控制动作。

调用Camera.open()的时候,如果相机在被另外一个程序利用,会抛出异常,所以要捕获这个异常:

private boolean safeCameraOpen(int id) {
    boolean qOpened = false;
  
    try {
        releaseCameraAndPreview();
        mCamera = Camera.open(id);
        qOpened = (mCamera != null);
    } catch (Exception e) {
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    }

    return qOpened;    
}

private void releaseCameraAndPreview() {
    mPreview.setCamera(null);
    if (mCamera != null) {
        mCamera.release();
        mCamera = null;
    }
}


在API 9之后,支持多相机。如果你使用的是之前的API,直接用没有参数的open()函数,你会得到一个后置的Camera对象。

创建相机预览视图

要拍摄一张照片之前,我们需要看到照片当前的预览图,在我们按下拍照按钮之前。可以利用SurfaceView来绘制当前相机传感器捕捉到的画面。

1.Preview类

要显示一个预览视图,那就需要一个对应的Preview类。这个类需要实现一个android.view.SurfaceHolder.Callback接口,用来专递相机捕获的图像数据给应用程序。

class Preview extends ViewGroup implements SurfaceHolder.Callback {

    SurfaceView mSurfaceView;
    SurfaceHolder mHolder;

    Preview(Context context) {
        super(context);

        mSurfaceView = new SurfaceView(context);
        addView(mSurfaceView);

        // Install a SurfaceHolder.Callback so we get notified when the
        // underlying surface is created and destroyed.
        mHolder = mSurfaceView.getHolder();
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }
...
}


在播放实时的画面之前,必须给这个类传递一个Camera对象,下面会讲到

2.设置和启动Preview

Camera对象和相关联的Preview必须按照指定的规则来创建,Camera是第一个被创建出来的。

下面的代码片段中,相机的初始化过程是被封装的,这样当用户改变相机的时候,setCamera()里面调用了Camera.startPreview()方法。preview也必须在它的类方法surfaceChanged()里面重新启动。

public void setCamera(Camera camera) {
    if (mCamera == camera) { return; }
    
    stopPreviewAndFreeCamera();
    
    mCamera = camera;
    
    if (mCamera != null) {
        List<Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
        mSupportedPreviewSizes = localSizes;
        requestLayout();
      
        try {
            mCamera.setPreviewDisplay(mHolder);
        } catch (IOException e) {
            e.printStackTrace();
        }
      
        /*
          Important: Call startPreview() to start updating the preview surface. Preview must 
          be started before you can take a picture.
          */
        mCamera.startPreview();
    }
}


修改相机设置

相机的设置决定了相机捕获图像的方法,如聚焦,曝光灯。下面的例子仅仅改变了预览的大小。可以查看相机的源码来获取更多的设置方法。

public void surfaceChanged(SurfaceHolder holder, int format, int w, int h) {
    // Now that the size is known, set up the camera parameters and begin
    // the preview.
    Camera.Parameters parameters = mCamera.getParameters();
    parameters.setPreviewSize(mPreviewSize.width, mPreviewSize.height);
    requestLayout();
    mCamera.setParameters(parameters);

    /*
      Important: Call startPreview() to start updating the preview surface. Preview must be
      started before you can take a picture.
    */
    mCamera.startPreview();
}


设置预览角度

许多的相机应用都默认的是风景模式,因为这个是相机传感器默认的显示方向。但是这并不妨碍你拍摄肖像模型的照片,因为这个角度是被记录在EXIF的头部。setCameraDisplayOrientation()方法可以让你修改照片的显示方向,但是并不影响照片的拍摄。在API 14和之前的API上,在改变方向之前, 必须停止preview,修改完成之后,在重新启动。

捕获一张照片

在preview启动之后,利用Camera.takePicture()方法拍摄一张照片。我们可以创建Camera.PictureCallback and Camera.ShutterCallback对象并传递他们给Camera.takePicture().

如果你想连续不断的抓去照片,创建一个Camera.PreviewCallback,实现onPreviewFrame()方法。介于2者之间,你也可以仅仅捕获选中的几个预览图像,或者是设置一个延迟操作来调用takePicture().

重新启动Preview

在拍摄一张照片之后,你必须重新启动preview来拍摄另外一张照片。下面的例子,重新是通过重载快门按钮来实现的。

@Override
public void onClick(View v) {
    switch(mPreviewState) {
    case K_STATE_FROZEN:
        mCamera.startPreview();
        mPreviewState = K_STATE_PREVIEW;
        break;

    default:
        mCamera.takePicture( null, rawCallback, null);
        mPreviewState = K_STATE_BUSY;
    } // switch
    shutterBtnConfig();
}


停止Preview和是否Camera对象

一般来说,当你的应用拍照完成后,要是否camera对象,不然的话,会导致其他的APP在用相机的时候产生崩溃,或者是你自己的APP在重新创建camera的实例的时候迆会崩溃。

那么什么时候释放相机呢?你的surfaceview被释放时一个很好的暗示信息,这个时候就应该去释放相机,像Preview类里面一样。

public void surfaceDestroyed(SurfaceHolder holder) {
    // Surface will be destroyed when we return, so stop the preview.
    if (mCamera != null) {
        /*
          Call stopPreview() to stop updating the preview surface.
        */
        mCamera.stopPreview();
    }
}

/**
  * When this function returns, mCamera will be null.
  */
private void stopPreviewAndFreeCamera() {

    if (mCamera != null) {
        /*
          Call stopPreview() to stop updating the preview surface.
        */
        mCamera.stopPreview();
    
        /*
          Important: Call release() to release the camera for use by other applications. 
          Applications should release the camera immediately in onPause() (and re-open() it in
          onResume()).
        */
        mCamera.release();
    
        mCamera = null;
    }
}


在前面的内容中,这个处理过程也是 setCamera()方法的一部分,所以初始化一个camera经常伴随着停止一个preview

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值