刚接触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的一些基本方法进行了封装,这样就方便了下次的使用,减少代码的重复量。
使用步骤:
- Obtain an instance of Camera from
.
- Get existing (default) settings with
.
- If necessary, modify the returned
object and call
.
- If desired, call
.
- Important: Pass a fully initialized
to
. Without a surface, the camera will be unable to start the preview.
- Important: Call
to start updating the preview surface. Preview must be started before you can take a picture.
- When you want, call
to capture a photo. Wait for the callbacks to provide the actual image data.
- After taking a picture, preview display will have stopped. To take more photos, call
again first.
- Call
to stop updating the preview surface.
- Important: Call
to release the camera for use by other applications. Applications should release the camera immediately in
(and re-
it in
).
以上是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