现在网上有很多博客对于Android自定义照相机这一块都有很透彻的讲解,不过为了留下自己对于Android自定义相机的理解,还是打算写几篇博客来记录它。本篇博客讲到的自定义照相机使用的是Android提供的旧版API即Camera,图像预览使用的是TextureView控件。
目前市面上的绝大部分手机应该都会支持拍照功能,但是为了适配所有情况并避免自己的应用安装到不支持拍照的手机上,那么需要在清单文件的manifest节点下添加如下代码:
<uses-feature android:name="android.hardware.Camera" android:required="true"/>
同时还需要如下权限:
//使用相机的权限
<uses-permission android:name="android.permission.CAMERA"/>
//往内存卡里面写的权限
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
//读取内存卡的权限
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
//使用相机录制视频的权限
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
2.1 是否支持前置拍照
/**
* 摄像机是否支持前置拍照
*
* @return
*/
public static boolean isSupportFrontCamera() {
//获取当前摄像头的数量
final int cameraCount = Camera.getNumberOfCameras();
Camera.CameraInfo info = new Camera.CameraInfo();
for (int i = 0; i < cameraCount; i++) {
Camera.getCameraInfo(i, info); //获取摄像头的信息
if (info.facing == 1) { //1 代表前置,0代表后置
return true;
}
}
return false;
}
2.2 是否支持闪光
/**
* 是否支持闪光
*
* @param context
* @return
*/
public static boolean isSupportFlashCamera(Context context) {
FeatureInfo[] features = context.getPackageManager().getSystemAvailableFeatures();
for (FeatureInfo info : features) {
if (PackageManager.FEATURE_CAMERA_FLASH.equals(info.name))
return true;
}
return false;
}
提到拍照那就避免不了图像的实时预览,本篇博客的图像预览采用的是TextureView。目前对TextureView的理解也就停留在会用上,所以这里就不做详细的介绍了。同时在Demo中自定义了一个相机的管理类CameraManager来维护Camera的使用。
3.1打开相机
在Activity生命周期的onResume()中来打开相机,打开相机的代码如下:
@Override
protected void onResume() {
super.onResume();
if (tvTexture.isAvailable()) {
//路径不为空,优先播放视频
if (recorderPath != null) {
playerManager.playMedia(new Surface(tvTexture.getSurfaceTexture()), recorderPath);
} else {
//使用管理类来打开相机
cameraManager.openCamera(this, tvTexture.getSurfaceTexture(),
tvTexture.getWidth(), tvTexture.getHeight());
}
} else {
//给TextureView对象设置SurfaceTextureListener的监听
tvTexture.setSurfaceTextureListener(this);
}
}
3.2 画面的缩放和聚焦
这里自定义了一个CameraView(继承至View)覆盖在TextureView上,通过重写CameraView的onTouch()方法捕获用户点击屏幕的位置来调用Camera相应的API来实现相机的聚焦和画面的缩放。其实也可以不写CameraView,直接继承TextureView同时重写它的onTouch()方法也可以达到同样的效果(翻看TextureView的源码可以发现它继承于View但并没有重写View的onTouch()方法),但是为了以后使用SurfaceView时容易拆分这里就重写了CameraView。
@Override
public boolean onTouchEvent(MotionEvent event) {
//保证拍照或者录制完视频后触摸CameraView不在进行聚焦或缩放
if (!isEnabled()) {
return super.onTouchEvent(event);
}
int action = MotionEventCompat.getActionMasked(event);
if (event.getPointerCount() == 1 && action == MotionEvent.ACTION_UP) {
float x = event.getX();
float y = event.getY();
//自定义方法,设置当前触摸点
setFoucsPoint(x, y);
if (listener != null) {
//通过监听触摸点来进行聚焦
listener.handleFocus(x, y);
}
} else if (event.getPointerCount() >= 2) {
switch (event.getAction() & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_POINTER_DOWN:
//自定义方法,计算两指之间的距离
oldDist = getFingerSpacing(event);
break;
case MotionEvent.ACTION_MOVE:
if (event.getPointerCount() < 2)
return true;
float newDist = getFingerSpacing(event);
if (this.listener != null) {
//通过监听触摸点来进行缩放
this.listener.handleZoom(false, oldDist, newDist);
oldDist = newDist;
}
break;
}
}
return true;
}
3.3 拍照
/**
* 拍照
*/
public void takePhoto(Camera.PictureCallback callback) {
if (mCamera != null) {
try {
//这里的三个参数意思特别简单翻看API很容易就查找到了,这里就不做介绍
mCamera.takePicture(null, null, callback);
} catch (Exception e) {
Toast.makeText(context, "拍摄失败", Toast.LENGTH_SHORT).show();
}
}
}
4.1 开始录制
/**
* 开始录制视频
*/
public void startMediaRecord(String savePath) {
if (mCamera == null || mProfile == null)
return;
//录制视频时必须调用,目的是解锁方便其他进程调用
mCamera.unlock();
if (mMediaRecorder == null) {
mMediaRecorder = new MediaRecorder();
//设置视频回放的输出方向,其参数必须是0,90,180,270中的一个
mMediaRecorder.setOrientationHint(90);
}
if (isCameraFrontFacing()) {
mMediaRecorder.setOrientationHint(270);
}
mMediaRecorder.reset();
mMediaRecorder.setCamera(mCamera);
mMediaRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
mMediaRecorder.setAudioSource(MediaRecorder.AudioSource.DEFAULT);
mMediaRecorder.setProfile(mProfile);
mMediaRecorder.setOutputFile(savePath);
try {
mMediaRecorder.prepare();
mMediaRecorder.start();
} catch (Exception e) {
e.printStackTrace();
}
}
4.2 结束录制
/**
* 停止录制
*/
public void stopMediaRecord() {
this.cameraType = 0;
//停止录制
stopRecorder();
//释放资源
releaseMediaRecorder();
}
未拍照或录像时相机效果图如下所示: