我们在开发的过程中,经常会要使用相机,android自己带有一个源生的app可以用来拍照,但是有时候为了更多的功能,我们需要自定义相机。
考虑点:
1、是否一定要用相机,没有相机的设备就不可以安装你的app了吗?
但是要记得再Manifest文件中声明,google play在没有相机的设备 上会自动过滤掉一定要使用相机的App。
2、你的应用将会怎样使用相机,调用系统已经存在的CameraApp,或者是自己定义一个App?调用系统的相机通过Intent就可以实现
3、图片存储,别的app是否可见?app卸载后是否还要可用?
Manifest文件配置
1、如果是通过intent调用相机,不需要申请权限。,通过添加Feature就可以满足
<uses-permission android:name="android.permission.CAMERA" />
2、自定义相机,则需要申请权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
在声明权限之后,google play就会阻止你的app安装在没有相机的设备上。
如果图片需要保存的在SD卡上,需要指明权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
使用已经存在的相机APP步奏:
1、构造一个Intent,使用Intent的类型为,MediaStore.ACTION_IMAGE_CAPTURE
调用一个已经存在的相机应用
Intent可以附带一个参数MediaStore.EXTRA_OUTPUT,参数类型为Uri指定一个路径和
文件名保存相片
2、调用startActivityForResult()
3、在activity的onActivityResult()方法中会收到来自Intent的数据
构建一个Intent对象,设置Action,并将保存图片路径传入
Intent cameraIntent = new Intent();
cameraIntent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
imageUri = getImageFileUri();
cameraIntent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);//指定了存储路径将返回的data将会是null
startActivityForResult(cameraIntent, REQUESt_CODE_CAPTURE_IMAGE);
private Uri getImageFileUri()
{
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date());
File imageFile = new File(FileUtil.EXAMPLE_PATH + Constant.IMAGE +File.separator + timeStamp + ".jpg");
return Uri.fromFile(imageFile);
}
在activity中的onAcivityResult方法中取数据,注意如果指定了保存路径,将不会有返回值,如果没有保存路径,则会返回路径。
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == RESULT_OK) {
switch (requestCode) {
case REQUESt_CODE_CAPTURE_IMAGE:
if (null != data)
{
}else
{
}
break;
}
}
}
2、自定义相机
遵循以下步奏:
1、检查是否有相机,访问请求
private boolean isCameraExist()
{
return getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA);
}
2、创建一个Activity类继实现SurfaceHolder接口
3、创建一个预览布局
4、创建动作监听,例如一个按钮拍照
5、拍照并保存
6、释放相机
相机功能:
android支持许多相机特点,例如相片格式,闪光灯模式,焦点设置,等等
这些都是通过Camera.Parameters来控制
这些知识点在代码中通过添加注释的方式来讲解。
开发过程中遇到的问题:
1、在onPause中释放相机后,onResume再为相机绑定SurfaceView的时候,会有异常抛出,相机已释放。
解决方案:添加变量hasSurface.在surfaceDestroyed中置为false。
2、闪光灯设置过程中Camera.Parameters.FLASH_MODE_ON无效果,
解决方案:改为Camera.Parameters.FLASH_MODE_TORCH
3、SurfaceHolder.Callback此接口是设置在SurfaceView中的。
4、使用相机前一定要判断是否支持相机,要在Manifest文件中配置,使用前置相机要记得判断是否支持前置相机。
5、人脸识别是在android 4.0之后添加的,并不是所有的android 4.0都支持人脸识别。笔者系统为 android 4.1,并不支持人脸识别。
6、最重要的一点,相机是有限的公共资源,很多app都可以调用,所以一定要release掉!
package com.example.androidtest;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
import com.example.constant.Constant;
import com.example.util.FileUtil;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.Face;
import android.hardware.Camera.FaceDetectionListener;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.Window;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
public class CameraActivity extends Activity implements OnClickListener ,SurfaceHolder.Callback{
private Camera mCamera;
private Parameters mParameters;
private SurfaceView mSurfaceView;
/**
* 相机位置
*/
private int cameraPosition = 0;
/**
* 这个参数主要是在程序到后台之后,再切换回来的判断
*/
private boolean hasSurface = true;
/**
* 闪光灯是否开启
*/
private boolean isLighting = false;
/**
* 是否支持闪光灯
*/
private boolean isSupportedLight = false;
/**
* 调整Zoom用的seekbar
*/
private SeekBar mSeekBar;
private SurfaceHolder viewHolder;
private int cameraCount,maxZoom;
@Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_camera);
hasSurface = false;
//获取相机个数
cameraCount = Camera.getNumberOfCameras();
}
@Override
protected void onResume() {
super.onResume();
initView();
}
private void initCamera(SurfaceHolder viewHolder)
{
mCamera = getCameraInstance(-1);
mCamera.setFaceDetectionListener(mFaceDetectionListener);//添加人脸识别监听
mParameters = mCamera.getParameters();
maxZoom = mParameters.getMaxZoom();
mSeekBar.setMax(maxZoom*100);
mSeekBar.setProgress((int) (0.5 * maxZoom * 100));
List features = mParameters.getSupportedFlashModes();//判断是否支持闪光灯
if (features.contains(Camera.Parameters.FLASH_MODE_ON)) {
isLighting = false;
isSupportedLight = true;
}
startPreviewOfCamera(viewHolder);
startFaceDectection();
}
private void startPreviewOfCamera(SurfaceHolder viewHolder)
{
try {
mCamera.setPreviewDisplay(viewHolder);
mCamera.setDisplayOrientation(90);//防止预览图片旋转
Parameters mParameters = mCamera.getParameters();
mParameters.setRotation(90);//防止保存的图片旋转
mCamera.setParameters(mParameters);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 初始化控件
* @date 2015年4月3日
* @time 下午5:13:35
* @author Tony
*/
private void initView()
{
mSurfaceView = (SurfaceView) findViewById(R.id.mSurfaceview);
viewHolder = mSurfaceView.getHolder();
//第一次会再onSurfaceCreated的时候初始化相机,并绑定预览效果
if (hasSurface) {
initCamera(viewHolder);
} else {
viewHolder.addCallback(this);//为viewHolder添加回调
viewHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
findViewById(R.id.btnOpenSplash).setOnClickListener(this);
findViewById(R.id.btnCaptureCamera).setOnClickListener(this);
findViewById(R.id.btnOpenFront).setOnClickListener(this);
mSeekBar = (SeekBar) findViewById(R.id.mSeekBar);
mSeekBar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() {
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
mParameters = mCamera.getParameters();
mParameters.setZoom((int) (progress * 1.0f / (maxZoom * 100) * maxZoom));
mCamera.setParameters(mParameters);
}
});
}
@Override
protected void onPause()
{
super.onPause();
releasedCamera();
}
/**
* 释放相机
* @date 2015年4月3日
* @time 下午5:17:24
* @author Tony
*/
private void releasedCamera()
{
if (null != mCamera)
{
mCamera.setPreviewCallback(null) ;
mCamera.stopPreview();
mCamera.release();
mCamera = null;
hasSurface = false;
}
}
@Override
public void onClick(View v) {
switch (v.getId())
{
case R.id.btnOpenSplash:
if (isSupportedLight)
{
String flashMode = isLighting ? Camera.Parameters.FLASH_MODE_OFF:Camera.Parameters.FLASH_MODE_TORCH;
mParameters.setFlashMode(flashMode);
mCamera.setParameters(mParameters);
isLighting = !isLighting;
}
break;
case R.id.btnOpenFront:
changeCamera();
break;
case R.id.btnCaptureCamera:
mCamera.takePicture(null, null, mPictureCallback);
break;
}
}
/**
* 拍照时候调用,保存图片
*/
private PictureCallback mPictureCallback = new PictureCallback()
{
@Override
public void onPictureTaken(byte[] data, Camera camera)
{
File imageFile = getImageFile();
if (null != imageFile)
{
try
{
FileOutputStream fos = new FileOutputStream(imageFile);
fos.write(data);
fos.close();
} catch (IOException e)
{
e.printStackTrace();
}
}
}
};
/**
* 获得保存图片的文件
* @return
* @date 2015年4月3日
* @time 下午5:18:03
* @author Tony
*/
@SuppressLint("SimpleDateFormat")
private File getImageFile()
{
String timeStamp = new SimpleDateFormat("yyyyMMdd_HHmmss").format(new Date()) + "TONY";
File imageFile = new File(FileUtil.EXAMPLE_PATH + Constant.IMAGE +File.separator + timeStamp + ".jpg");
return imageFile;
}
/**
* 获得相机实例
* @param cameraPosition
* @return
* @date 2015年4月3日
* @time 下午5:15:25
* @author Tony
*/
public Camera getCameraInstance(int cameraPosition)
{
if (cameraPosition < 0 || cameraPosition > cameraCount) {
cameraPosition = 0;
}
Camera mCamera = null;
try
{
mCamera = Camera.open(cameraPosition);
}
catch (Exception e)
{
//相机别的app在使用,或者是不存在
}
return mCamera;
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
if (!hasSurface) {
hasSurface = true;
if (mCamera == null) {
initCamera(holder);
}
mCamera.startPreview();
}
hasSurface = true;
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
hasSurface = false;
}
private void changeCamera()
{
if (cameraCount <= 1) {
return;
}
mCamera.stopPreview();
mCamera.release();
mCamera = null;
cameraPosition = cameraPosition == 1 ? 0:1;
mCamera = getCameraInstance(cameraPosition);
mCamera.setFaceDetectionListener(mFaceDetectionListener);
startPreviewOfCamera(viewHolder);
mCamera.startPreview();
mCamera.startFaceDetection();
}
private void startFaceDectection()
{
// Try starting Face Detection
Camera.Parameters params = mCamera.getParameters();
// start face detection only *after* preview has started
if (params.getMaxNumDetectedFaces() > 0){
mCamera.startFaceDetection();
}
}
private FaceDetectionListener mFaceDetectionListener = new FaceDetectionListener()
{
@Override
public void onFaceDetection(Face[] faces, Camera camera) {
if (faces.length > 0) {
Log.e("====人脸识别====","" + faces[0].leftEye.x );
}
}
};
}
布局文件就不贴了吧。很简单,一个SurfaceView,三个button,还有一个SeekBar