公司业务需要做一个静默拍照的功能,了解了一下常见的解决方案,基本上都是基于SurfaceView做的,弊病颇多。研究了一下,决定以SurfaceTexture为切入点,做一个真正的静默拍照功能。
public class xxx implements SurfaceTexture.OnFrameAvailableListener{
private static final String TAG = "slient_camera";
private Context context;
private Camera mCamera;
private SurfaceTexture surfaceTexture;
public xxx(Context context) {
this.context = context;
}
public void onCreate() {
Log.i(TAG, " onCreate()");
surfaceTexture = new SurfaceTexture(10);
surfaceTexture.setOnFrameAvailableListener(this);
//openCamere();
}
public boolean openCamere() {
try {
mCamera = Camera.open(0);
} catch (Exception e) {
e.printStackTrace();
return false;
}
mCamera.setDisplayOrientation(90);
Camera.Parameters params = mCamera.getParameters();
//设置camera各种属性,略
……
mCamera.setParameters(params);
if (mCamera == null) {
// Seeing this on Nexus 7 2012 -- I guess it wants a rear-facing camera, but
// there isn't one. TODO: fix
//throw new RuntimeException("Default camera not available");
Log.e(TAG, "openCamere, mCamera == null!");
return false;
}
try {
mCamera.setPreviewTexture(surfaceTexture);//用surfaceTexture做预览
mCamera.startPreview();
} catch (IOException ioe) {
ioe.printStackTrace();
return false;
}
return true;
}
public void tackPicture() {
//Log.w(TAG, "tackPicture()");
File picFile = CameraUtil.getOutputMediaFile();
if (picFile == null) {
Log.e(TAG, "tackPicture, getOutputMediaFile is null!");
return;
}
if (null == mCamera) {
Log.e(TAG, "tackPicture(), null == mCamera");
return;
}
mCamera.takePicture(mShutterCallback, null, mPictureCallback);
//saveData = true;
//Log.i(TAG, "takePicture after time = " + System.currentTimeMillis());
}
private Camera.ShutterCallback mShutterCallback = new Camera.ShutterCallback() {
@Override
public void onShutter() {
//静默拍照,此处不能播放自定义拍照音
}
};
private Camera.PictureCallback mPictureCallback = new Camera.PictureCallback() {
@Override
public void onPictureTaken(final byte[] data, Camera camera) {
//将data转成bitmap即可
//其他操作
closeCamera();
}
};
@Override
public void onFrameAvailable(SurfaceTexture surfaceTexture) {
//surfaceTexture.updateTexImage();
}
private void closeCamera() {
Log.i(TAG, "closeCamera");
if (null != mCamera) {
mCamera.stopPreview();
mCamera.release();
mCamera = null;
}
}
public void onDestroy() {
closeCamera();
}
}
主要代码都在上面了,大概的解释一下:创建一个SurfaceTexture,这个SurfaceTexture承载了相机的预览。但是由于我们没有设置可见的TextureView,所以不会有预览的界面。关于openCamere这一段,其实是有很多坑的,比如相机前后摄像头、预览角度、预览界面之类的设置,不赘述了。拍照时调用tackPicture方法,mPictureCallback 方法内的那个byte数组就是最终的照片,将其转化为bitmap即可。具体的实现很简单,就不再贴出来了。
总结一下这个思路,好处在于不需要设置一个可见的view去承载相机的预览,把这个事情交给了surfaceTexture去做,可以全程在后台静默完成,想想还是挺猥琐的。限制在于surfaceTexture只能在API11之上才能调用,而在更高等级的API21中,调用相机需要弹出权限提示框,无法再静默打开摄像头。说白了,这是一个只能在API11~API21中间使用的思路。