Android 自定义Camera基本使用步骤及关键注意点

刚接触Camera这一块的开发,看了两天的API,终于搞懂了Camera的一些基本功能使用,编写博客记录一下,也分享些经验给同学们。

1. Camera的生命周期

    open()---->startPreview()---------------------->stopPreview()---->release()--------->{open()}[再次使用]

    /**
     * 开启Camera
     * @param cameraType
     * @return
     */
    public Camera openCamera(Consts.CameraType cameraType){
        Camera mCamera = null;
        int cameraId = getCameraId(cameraType);
        mCamera = Camera.open(cameraId);
        Log.i(TAG,"camera open");
        return mCamera;
    }

    /**
     * 开启Camera的预览
     * @param camera
     */
    public void startCameraPreview(Camera camera){
        if (camera==null||isCameraPreviewStart){
            return;
        }
        camera.startPreview();
        Log.i(TAG,"camera start");
        isCameraPreviewStart = true;
    }

    /**
     * 结束Camera的预览并释放
     * @param camera
     */
    public void stopCameraPreview(Camera camera){
        if (camera==null||!isCameraPreviewStart){
            return;
        }
        camera.stopPreview();
        camera.release();
        Log.i(TAG,"camera stop");
        isCameraPreviewStart = false;
    }

   我在这里对Camera的一些基本方法进行了封装,这样就方便了下次的使用,减少代码的重复量。

    使用步骤:

  1. Obtain an instance of Camera from open(int).
  2. Get existing (default) settings with getParameters().
  3. If necessary, modify the returned Camera.Parameters object and call setParameters(Camera.Parameters).
  4. If desired, call setDisplayOrientation(int).
  5. Important: Pass a fully initialized SurfaceHolder to setPreviewDisplay(SurfaceHolder). Without a surface, the camera will be unable to start the preview.
  6. Important: Call startPreview() to start updating the preview surface. Preview must be started before you can take a picture.
  7. When you want, call takePicture(Camera.ShutterCallback, Camera.PictureCallback, Camera.PictureCallback, Camera.PictureCallback) to capture a photo. Wait for the callbacks to provide the actual image data.
  8. After taking a picture, preview display will have stopped. To take more photos, call startPreview() again first.
  9. Call stopPreview() to stop updating the preview surface.
  10. 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()). 

   以上是android.hardware.camera的api使用步骤。(英语水平不高的同学们就百度翻译一下再看啦O(∩_∩)O哈!~我就是翻译后看的~)

  注意:

     1.在调用Camera的open()方法之前,程序员必须要对使用的手机设备进行检测,判断该设备是否拥有相机这一功能特征:如果没有的话,则直接return,结束此程序;如果有的话,再调用open()方法。(一般的智能机都有相机的) 这里附上判断的代码--使用hasSystemFeature的方法检测

public boolean checkDevicesHasCamera(Context context){
        return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)
         || context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA_FRONT);
}

    2.open()方法在API中有两个:open() 和open(int cameraId)  第一次无参数的不用多说,关键是第二个open(int cameraId),这里的cameraId表示相机的id,为什么相机还有id呢?这是因为有些手机不仅仅只有一个摄像头,其包括了前置摄像头和后置摄像头(还有些手机不止这两个)。因此,系统需要根据你传给它的cameraId来open前置还是后置摄像头,也就是说,你还需要获取前置/后置摄像头的cameraId来帮助你实现open相应设备。这里附上获取cameraId的代码(CameraType是我写的一个枚举变量,里面有CameraBack和CameraFront,分别标志前置和后置)

private int getCameraId(Consts.CameraType cameraType){
        int cameraId = -1,back_id=-1,front_id=-1;
        int counts = Camera.getNumberOfCameras();
        for (int i = 0; i < counts; i++) {
            Camera.CameraInfo  cameraInfo= new Camera.CameraInfo();
            Camera.getCameraInfo(i,cameraInfo);
            if (cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_BACK) back_id = i;
            if (cameraInfo.facing==Camera.CameraInfo.CAMERA_FACING_FRONT) front_id = i;
        }
        cameraId = (cameraType== Consts.CameraType.CameraBack)?back_id:front_id;
        return cameraId;
}

    3.使用Camera,我们不可能只是预览界面而已,可能还有很多操作:比如说拍照/聚焦/开启闪光灯等等,而这些功能的实现,都必须是在startPreview()-----------------------stopPreview()之间执行的。[关键!]

2.使用Camera

    2.1 创建相机预览窗口

           在生命周期中我们看到需要调用startPreview(),也就是启动预览,那么预览的东西在哪里呢?这是需要你自己亲手创建的!这里教大家两种方法:

            方法一:自定义view 继承与surfaceView 并实现其SurfaceHolder.CallBack接口,然后将该view

添加到你创建的预览布局中(这个不用解释吧,view只有放置在布局里面,才能关联到activity中去)。这里附上代码

public class CameraPreview extends SurfaceView implements SurfaceHolder.Callback{

    public SurfaceHolder mHolder;
    private Camera mCamera;

    //创建一个surface来preview camera
    public CameraPreview(Context context,Camera camera) {
        super(context);
        mHolder = this.getHolder();
        mCamera = camera;
        mHolder.addCallback(this);
        mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
    }

    public SurfaceHolder getSurfaceHolder(){
        if (mHolder!=null){
            return mHolder;
        }else {
            return null;
        }
    }

    @Override
    public void surfaceCreated(SurfaceHolder surfaceHolder) {
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();//启动preview
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    @Override
    public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
        if (mHolder==null){
            return;
        }
        //当surface发生改变的时候,先关闭掉preview,然后再次启动
        mCamera.stopPreview();
        try {
            mCamera.setPreviewDisplay(mHolder);
            mCamera.startPreview();//启动preview
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
        System.out.println("surfaceDestroyed");
    }

}

            方法二: 在布局文件中直接使用TextureView然后为TextureView设置SurfaceTextureListener监听事件最后使用Camera.setPreviewTexture(TextureView.getSurfaceTexture)即可 【推荐方法】 这里附上代码

private TextureView.SurfaceTextureListener mSurfaceListener = new TextureView.SurfaceTextureListener() {
        @Override
        public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) {
            mSurfaceTexture = surfaceTexture;
            cameraUtils.setCameraParameter(camera,cameraType);
            cameraUtils.setCameraPreviewSize(camera,screenWidth,screenHeight);
            try {
                camera.setPreviewTexture(mSurfaceTexture);
                cameraUtils.startCameraPreview(camera);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        @Override
        public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {

        }

        @Override
        public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {
            cameraUtils.stopCameraPreview(camera);
            return true;
        }

        @Override
        public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {
        }
};

textureView = (TextureView) findViewById(R.id.textureView);
textureView.setSurfaceTextureListener(mSurfaceListener);
        

            注意:

                1.在cameraUtils.setCameraParameter(camera,cameraType)方法中,需要将camera的预览显示状况进行顺时针的旋转90度,因为在Camera中默认显示的是横屏的画面,就是将画面逆时针旋转了90度,因此我们在startPreview之前,要将该画面顺时针转回来。

                 2.cameraUtils.setCameraPreviewSize(camera,screenWidth,screenHeight)方法的目的是避免预览界面出现“失真”----拉伸 状况。这是一个关键点,这个大家可以下载我后面给出的链接查看,也可以等我下一次发布博客进行详解。

    2.2 进行拍照保存功能

            在启动了预览功能之后,即startPreview()之后,stopPreview()之前,我们可以执行一系列的相关操作(这个问题我强调了很多遍,大家必须要记住,千万不要犯那个stopPreview了或者release()了之后,还对camera进行操作)。 而拍照功能,camera API中提到了一个方法 takePicture(),我们实现这个方法即可实现拍照功能。

             步骤如下:

  • 首先需要明确我们拍照所得到的图片保存到本地的什么位置,即本地保存目录。这里附上代码
public static File getOutputImageFile(){
        File mediaStorageDir = new File(Environment.getExternalStorageDirectory().getAbsolutePath()+"/Pictures");
        if (!mediaStorageDir.exists()){
            if (!mediaStorageDir.mkdir()){
                System.out.println("创建文件夹失败");
            }
        }
        // Create a media file name
        String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
        File mediaFile = new File(mediaStorageDir.getPath() + File.separator +
                "IMG_"+ timeStamp + ".jpg");
        System.out.println(mediaFile.getAbsolutePath());
        return mediaFile;
}
  • 接下来就是实现takePicture方法了
camera.takePicture(null, null, null, new Camera.PictureCallback() {
            @Override
            public void onPictureTaken(final byte[] bytes, final Camera camera) {
                final File file = BaseUtils.getOutputImageFile();
                final String filePath = file.getPath();
                new Thread(new Runnable() {
                    FileOutputStream fops = null;
                    @Override
                    public void run() {
                        try {
                            fops = new FileOutputStream(filePath);
                            fops.write(bytes);
                        } catch (FileNotFoundException e) {
                            e.printStackTrace();
                        } catch (IOException e) {
                            e.printStackTrace();
                        } finally {
                            if (fops!=null){
                                try {
                                    fops.close();
                                } catch (IOException e) {
                                    e.printStackTrace();
                                }
                            }
                        }
                    }
                }).start();
            }
});
  • 在takePicture之后,系统会在内容调用stopPreview方法,即介绍了这一次的camera操作,但是我们不可能一次启动这个应用只拍一张照片就结束了的,因此需要重新启动camera
private void restartCamera() {
        cameraUtils.stopCameraPreview(camera);
        camera = cameraUtils.openCamera(cameraType);
        try {
            camera.setPreviewTexture(mSurfaceTexture);
        } catch (IOException e) {
            e.printStackTrace();
        }
        cameraUtils.setCameraParameter(camera,cameraType);
        cameraUtils.startCameraPreview(camera);
}

到这里,一个基本的自己创建的相机功能已经基本完成了!希望大家能够写出属于自己的Camera应用,而不是简单地调用系统的相机!

这里附上本人的Demo链接 http://git.oschina.net/meinkonone/CameraTest

转载于:https://my.oschina.net/mkonone/blog/793153

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值