Android知识要点整理----控制相机

Posted znapast

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android知识要点整理----控制相机相关的知识,希望对你有一定的参考价值。

除了调用第三方APP进行拍照外,我们还可以自己使用系统API来控制相机设备进行拍照。这个相对来说比较复杂。下面只讲比较关键的步骤。

1.声明权限

控制照相机我们需要声明使用照相设备的权限。

<uses-feature android:name="android.hardware.camera" android:required="true"/> 
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />

我们声明feature的required属性为true,这样没有相机设备的手机就没法安装APP。若设置为false,我们就需要在代码中检测是否具有相机设备,若有的话则开启拍照功能,否则需要禁用该功能。判断方法如下:

if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA))
    photoBtn.setOnClickListener(this);
else
    photoBtn.setVisibility(View.GONE);


if(getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_AUTOFOCUS))
            //开启自动聚焦模块

2.打开相机设备

打开相机设备就是获得相机的一个实例,它是IO操作,所以可能比较耗时。为了避免ANR,我们需要在onCreateonResume方法中另开一个线程去执行IO操作。相机设备可能会被其他APP占用,这时调用Camera.open()方法会抛出异常。安全地打开相机设备的代码如下:

@Override
protected void onResume() 
    super.onResume();
    //另开一个线程
    new Thread(new Runnable() 
        @Override
        public void run() 
            if(safeCameraOpen(0))
                mHandler.post(new Runnable() 
                    @Override
                    public void run() 
                        if(mPreview != null)
                        //设置给预览视图,mPreview 为SurfaceView的子类
                            mPreview.setCamera(mCamera);
                        
                    
                );
            
        
    ).start();


/**
 *打开指定的相机设备,id为相机序号,<=相机设备数量-1
 **/
private boolean safeCameraOpen(int id) 
    boolean qOpened = false;
    try 
        //确保之前的实例已经被正确关闭并释放
        releaseCameraAndPreview();
        mCamera = Camera.open(id);
        qOpened = (mCamera != null);
        if(mCamera != null)
            mCamera.setDisplayOrientation(90);
        
     catch (Exception e) 
        Log.e(getString(R.string.app_name), "failed to open Camera");
        e.printStackTrace();
    
    return qOpened;


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

上面的代码在onResume方法中安全的打开设备。同样重要的是,我们要在onPause方法中确保相机设备被正确的关闭和释放,这样就不会阻碍其他APP使用相机设备。

 @Override
 protected void onPause() 
    releaseCameraAndPreview();
    super.onPause();

3.关联相机和预览视图

要在拍照前预览效果,我们要构造预览视图,这个视图通常是继承自SurfaceView的自定义视图。SurfaceView能够非UI线程中绘制界面,因为预览效果需要频繁更新界面,使用它可以避免阻塞UI线程。下面是自定义的CameraPreview的完整代码:

package com.github.znacloud.multimediademo.view;

import android.content.Context;
import android.hardware.Camera;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

import java.io.IOException;
import java.util.List;

/**
 * Created by Stephan on 2016/1/15.
 */
public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback
    private SurfaceHolder mHolder;
    private Camera mCamera;
    private List<Camera.Size> mSupportedPreviewSizes;

    public CameraPreview(Context context) 
        super(context);
        inits(context);
    

    public CameraPreview(Context context, AttributeSet attrs) 
        super(context, attrs);
        inits(context);
    

    public CameraPreview(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
        inits(context);
    

    private void inits(Context pContext)
        mHolder = getHolder();
        mHolder.addCallback(this);
    

    @Override
    public void surfaceCreated(SurfaceHolder holder) 

    

    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) 
        if(mCamera == null) return;
        Camera.Parameters parameters = mCamera.getParameters();
        //根据视图的尺寸设置预览图的尺寸
        parameters.setPreviewSize(width, height);
        mCamera.setParameters(parameters);
        requestLayout();

        // 必须调用startPreview里面更新视图
        mCamera.startPreview();
    

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) 
        if (mCamera != null) 
            // 视图销毁时停止预览
            mCamera.stopPreview();
        
    

    public void setCamera(Camera camera) 
        if (mCamera == camera)  return; 

        //设置新的相机实例之前确保旧的相机实例被正确的释放和关闭
        stopPreviewAndFreeCamera();

        mCamera = camera;

        if (mCamera != null) 
            List<Camera.Size> localSizes = mCamera.getParameters().getSupportedPreviewSizes();
            //TODO:根据设备支持的预览尺寸设置视图尺寸

            try 
                //关联到SurfaceView
                mCamera.setPreviewDisplay(mHolder);
             catch (IOException e) 
                e.printStackTrace();
            

            //每次更新了相机实例后都要调用此方法开启预览功能
            mCamera.startPreview();
        
    

    private void stopPreviewAndFreeCamera() 
        if (mCamera != null) 
            // 停止预览
            mCamera.stopPreview();

            // 释放设备,这样其他APP就可以继续使用相机设备
            mCamera.release();

            mCamera = null;
        
    

接下来就是在成功打开相机设备后将相机实例关联到预览视图。

if(mPreview != null)
    mPreview.setCamera(mCamera);
try 
    mCamera.setPreviewDisplay(mHolder);
 catch (IOException e) 
    e.printStackTrace();

4.拍摄照片

上面的工作做完了之后我们就可以正式拍摄照片了。使用Camera.takePicture()方法可以执行拍摄照片的动作,该方法可以传入多个回调函数。看API文档:

我们现在只做最简单的拍照,实现代码如下:

 private static final int K_STATE_FROZEN = 0;
 private static final int K_STATE_PREVIEW = 1;
 private static final int K_STATE_BUSY = 2;

 ......

 mCaptureBtn = (Button)findViewById(R.id.btn_shutter);
 mCaptureBtn.setOnClickListener(new View.OnClickListener() 
    @Override
    public void onClick(View v) 
        if(mPreviewState == K_STATE_FROZEN)
            mCamera.startPreview();
            mPreviewState = K_STATE_PREVIEW;
        else if(mPreviewState == K_STATE_PREVIEW)
            mCamera.takePicture(null,rawCallBack,null);
            mPreviewState = K_STATE_BUSY;
        
    
);

由于在拍照完成后相机会自动进入冻结状态,我们需要根据状态来重新启动预览状态。上面的代码使用了拍照按钮来响应两种不同的操作,若当前为预览状态,点击按钮将立即拍照,此后相机进入冻结状态,再次点击按钮就只是恢复预览状态,不执行拍照。拍照时我们只传了一个回调函数rawCallBack,该回调类定义如下:

private Camera.PictureCallback rawCallBack = new Camera.PictureCallback() 
    @Override
    public void onPictureTaken(byte[] data, Camera camera) 
        mPreviewState = K_STATE_FROZEN;
        //TODO:data中保存的是照片的原始数据,我们可以对其进行处理,保存为照片或者其他事情
    
;

到此为止,我们已经控制相机拍摄一张完整的照片了。当然,想要界面更加友好,更易使用,我们需要添加一些细节,比如添加快门声,添加聚焦效果,添加人脸检测功能等等。这些功能,系统都有提供API,我们只需要实现响应的界面反馈出来就行了,这里就不详细介绍了。

以上是关于Android知识要点整理----控制相机的主要内容,如果未能解决你的问题,请参考以下文章

Android知识要点整理----Bitmap图片处理和展示

Android知识要点整理----音频播放管理

Android 知识要点整理(13)----网络连接

android知识要点整理(14)----Volley(HTTP请求框架)

Android知识要点整理(17)----Gradle 之自定义构建

Android知识要点整理(17)----Gradle 之自定义构建