Android 中自定义照相机Camera详解
要想定义一个照相机需要用到两个核心类:Camera和SurfaceView
Camera类一些API
Camera用于管理和操作camera资源,他提供了完整的相机底层接口。可以通过camera.getParameters()来获取Camera的参数用以设置照片的预览尺寸、拍摄尺寸、预览帧、拍摄帧、设置光圈、曝光、聚焦、颜色特效等。
SurfaceView类
用于绘制相机的预览功能的类,提供给用户的实时预览的图像。普通的view以及派生类都是共享同一个surface的,所有的绘制都是在UI线程之中绘制的。而surfaceview是一种比较特殊的view,他并不与其他的普通的view共享surface,而是在内部持有了一个独立的surface,surfaceview负责管理这个surface的格式,尺寸及显示位置。由于UI线程还要和其他的线程交互逻辑,因此对view的更新速度和帧率无法保证,而surfaceview由于持有一个独立的surface,因而可以在独立的线程中进行绘制,提供更高的帧率。自定义相机的预览图像由于对更新速度和帧率要求比较高,所以比较实用surfaceview来显示。
SurfaceHolder
surfaceholder是控制surface的一个抽象接口,他能够控制surface的尺寸和格式,修改surface的像素,监视surface的变化等,surface的典型应用就是用于surfaceview中。surfaceview通过getHolder()方法获得surfaceholder实例,通过后者来监听管理surface的状态。
SurfaceHolder.Callback接口
负责监听surface状态变化的接口,有三个方法:
surfaceCreated(SurfaceHolder holder)
在surface创建后立即被调用。在开发自定义相机时,可以通过重载这个函数调用camera.open()、camera.setPreviewDisplay(),来实现获取相机的资源、链接camera和surface等操作。
surfaceChanged(SurfaceHolder holder,int format,int width,int height)
在surface发生format或size变化时调用。在开发自定义相机时,可以通过重载这个函数调用camera.startPreview来开启相机预览,使得camera预览帧数据可以传递给surface,从而实时显示相机预览图像。
surfaceDestory(SurfaceHolder holder)
在surface销毁之前被调用。在开发自定义相机时,可以通过重载这个函数调用camera.stopPreview(),camera.release()来实现停止相机预览及释放相机资源等操作。
public class CustomCameraActivity extends Activity implements OnClickListener,
SurfaceHolder.Callback, Camera.PictureCallback {
private Camera camera;
private static final String TAG = "Havorld";
private Camera.Parameters parameters;
private int orientationDegrees = 90;
private FrameLayout frameLayout;
private ImageButton imageButton, reset, ok;
/** 路径: /storage/emulated/0/Pictures/ */
private String savePath;
private String path;
private SurfaceHolder surfaceHolder;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
savePath = Environment
.getExternalStoragePublicDirectory(Environment.DIRECTORY_PICTURES)
+ File.separator;
SurfaceView surfaceView = (SurfaceView) findViewById(R.id.surfaceView);
surfaceView.setOnClickListener(this);
frameLayout = (FrameLayout) findViewById(R.id.frameLayout);
imageButton = (ImageButton) findViewById(R.id.imageButton);
reset = (ImageButton) findViewById(R.id.reset);
ok = (ImageButton) findViewById(R.id.ok);
frameLayout.setOnClickListener(this);
imageButton.setOnClickListener(this);
reset.setOnClickListener(this);
ok.setOnClickListener(this);
// 到SurfaceHolder,SurfaceHolder相当于一个监听器,可以通过CallBack来监听 SurfaceView上的变化。
surfaceHolder = surfaceView.getHolder();
surfaceHolder.addCallback(this);
// 为了实现照片预览功能,需要将SurfaceHolder的类型设置为PUSH,这样画图缓存就由Camera类来管理,画图缓存是独立于Surface的
surfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
startOrientationChangeListener();
}
private final void startOrientationChangeListener() {
OrientationEventListener mOrEventListener = new OrientationEventListener(
this) {
@Override
public void onOrientationChanged(int rotation) {
if (((rotation >= 0) && (rotation <= 45)) || (rotation >= 315)) {
orientationDegrees = 90;
} else if ((rotation > 45) && (rotation < 135)) {
orientationDegrees = 180;
} else if ((rotation >= 135) && (rotation <= 225)) {
orientationDegrees = 270;
} else if ((rotation > 225) && (rotation < 315)) {
orientationDegrees = 0;
}
// Log.e(TAG, "rotation:"+rotation);
}
};
mOrEventListener.enable();
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
Log.e(TAG, "surfaceCreated");
surfaceHolder = holder;
initCamera();
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.e(TAG, "surfaceChanged");
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.e(TAG, "surfaceDestroyed");
// 当Surface被销毁的时候,该方法被调用
// 在这里需要释放Camera资源
releaseCamera();
}
@SuppressWarnings("deprecation")
private void initCamera() {
// 判断是否有摄像头
if (!getPackageManager()
.hasSystemFeature(PackageManager.FEATURE_CAMERA))
return;
// 获取摄像头的个数
// int numberOfCameras = Camera.getNumberOfCameras();
// 后置摄像头
camera = Camera.open(Camera.CameraInfo.CAMERA_FACING_BACK);
/**
* 获取设备Camera特性参数。由于不同的设备Camera的特性是不同的,所以在设置时需要首先判断设备对应的特性,再加以设置
*/
parameters = camera.getParameters();
// 获取可预览的尺寸集合
List<Size> supportedPreviewSizes = parameters
.getSupportedPreviewSizes();
Size previewSize = PreviewSizeUtil
.getSupportSize(supportedPreviewSizes);
// 获取可设置图片的大小集合
List<Size> supportedPictureSizes = parameters
.getSupportedPictureSizes();
// 设置生成最大的图片
// Size pictureSize =
// PreviewSizeUtil.getSupportSize(supportedPictureSizes);
Size pictureSize = supportedPictureSizes.get((supportedPictureSizes
.size() - 1) / 2);
// 获取可设置的帧数
List<Integer> supportedPreviewFrameRates = parameters
.getSupportedPreviewFrameRates();
Integer frameRates = supportedPreviewFrameRates
.get((supportedPreviewFrameRates.size() - 1) / 2);
// 设置Camera的参数
parameters.setPreviewSize(previewSize.width, previewSize.height);
parameters.setPictureSize(pictureSize.width, pictureSize.height);
// 设置帧数(每秒从摄像头里面获得几个画面)
parameters.setPreviewFrameRate(frameRates);
// 设置图片格式
parameters.setPictureFormat(ImageFormat.JPEG);
// 设置照片质量
parameters.setJpegQuality(100);
// 首先获取系统设备支持的所有颜色特效,如果设备不支持颜色特性将返回一个null, 如果有符合我们的则设置
List<String> colorEffects = parameters.getSupportedColorEffects();
Iterator<String> colorItor = colorEffects.iterator();
while (colorItor.hasNext()) {
String currColor = colorItor.next();
if (currColor.equals(Camera.Parameters.EFFECT_SOLARIZE)) {
// parameters.setColorEffect(Camera.Parameters.EFFECT_AQUA);
break;
}
}
// 获取对焦模式
List<String> focusModes = parameters.getSupportedFocusModes();
// [auto, infinity, macro, continuous-video, continuous-picture, manual]
if (focusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
// 设置自动对焦
parameters.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
}
// 设置闪光灯自动开启
List<String> flashModes = parameters.getSupportedFlashModes();
if (flashModes.contains(Camera.Parameters.FLASH_MODE_AUTO)) {
// 自动闪光
parameters.setFlashMode(Camera.Parameters.FLASH_MODE_AUTO);
}
int orientationDegrees = PreviewSizeUtil.getCameraDisplayOrientation(
this, Camera.CameraInfo.CAMERA_FACING_BACK);
// 设置相机镜头旋转角度(默认摄像头是横拍)
camera.setDisplayOrientation(orientationDegrees);
try {
// 设置显示
camera.setPreviewDisplay(surfaceHolder);
} catch (IOException exception) {
releaseCamera();
}
// 设置完成需要再次调用setParameter方法才能生效
camera.setParameters(parameters);
// 开始预览
camera.startPreview();
}
private void releaseCamera() {
if (camera != null) {
camera.setPreviewCallback(null);
camera.stopPreview();
camera.release();
camera = null;
}
}
@Override
public void onPictureTaken(byte[] data, Camera camera) {
path = savePath + "XiuMF_" + System.currentTimeMillis() + ".jpg";
File file = new File(path);
OutputStream output = null;
try {
output = new FileOutputStream(file);
output.write(data);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (output != null) {
try {
output.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
// 拍照后预览会停止,重新开启下
camera.startPreview();
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.frameLayout:
case R.id.imageButton:
if (parameters != null) {
// 设置照片在相册的旋转角度(默认摄像头是横放)
// parameters.setRotation(orientationDegrees);
parameters.set("rotation", orientationDegrees);
// 获取当前手机屏幕方向
camera.setParameters(parameters);
Log.e(TAG, "orientationDegrees:" + orientationDegrees);
}
/**
* ShutterCallback shutter:按下快门的回调 PictureCallback raw:原始图像数据的回调
* PictureCallback postview:压缩图像 PictureCallback
* jpeg:压缩成jpg格式的图像数据的回调
*/
camera.takePicture(null, null, null, this);
break;
case R.id.ok:// 获取照片
Intent intent = getIntent();
if (intent != null) {
intent.putExtra("path", path);
setResult(-1, intent);
}
finish();
break;
case R.id.reset: // 删除照片
File file = new File(path);
if (file.exists()) {
file.delete();
}
file = null;
break;
default:
break;
}
}
@Override
protected void onDestroy() {
super.onDestroy();
releaseCamera();
}
}
PreviewSizeUtil类
public class PreviewSizeUtil {
/**
* 获取最大的预览/图片尺寸
*
* @return 预览尺寸集合
*/
public static Size getSupportSize(List<Size> listSize) {
if (listSize == null || listSize.size() <= 0) {
return null;
}
Size largestSize = listSize.get(0);
if (listSize.size() > 1) {
Iterator<Camera.Size> iterator = listSize.iterator();
while (iterator.hasNext()) {
Camera.Size size = iterator.next();
if (size.width > largestSize.width && size.height > largestSize.height) {
largestSize = size;
}
}
}
return largestSize;
}
/**
* 获取拍照时偏移方向的角度(此方法在手机方向没有锁定,为自由切换时易用)
*
* @param activity
* @param cameraId 开启相机的id(0.后相机,1.前相机)
* @return 偏移的角度
*/
public static int getCameraDisplayOrientation(Activity activity,
int cameraId) {
CameraInfo info = new Camera.CameraInfo();
Camera.getCameraInfo(cameraId, info);
int rotation = activity.getWindowManager().getDefaultDisplay()
.getRotation();
int degrees = 0;
switch (rotation) {
case Surface.ROTATION_0:
degrees = 0;
break;
case Surface.ROTATION_90:
degrees = 90;
break;
case Surface.ROTATION_180:
degrees = 180;
break;
case Surface.ROTATION_270:
degrees = 270;
break;
}
int result;
if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
result = (info.orientation + degrees) % 360;
result = (360 - result) % 360; // compensate the mirror
} else { // back-facing
result = (info.orientation - degrees + 360) % 360;
}
return result;
}
}