刚接触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
open(int)
. - Get existing (default) settings with
getParameters()
. - If necessary, modify the returned
Camera.Parameters
object and callsetParameters(Camera.Parameters)
. - If desired, call
setDisplayOrientation(int)
. - Important: Pass a fully initialized
SurfaceHolder
tosetPreviewDisplay(SurfaceHolder)
. Without a surface, the camera will be unable to start the preview. - Important: Call
startPreview()
to start updating the preview surface. Preview must be started before you can take a picture. - 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. - After taking a picture, preview display will have stopped. To take more photos, call
startPreview()
again first. - Call
stopPreview()
to stop updating the preview surface. - Important: Call
release()
to release the camera for use by other applications. Applications should release the camera immediately inonPause()
(and re-open()
it inonResume()
).
以上是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