刚接触Android三个月,以下内容可能存在错误,欢迎指正。
以下在Android5. 1 ,API22机器下调试的。
之前参考网上的资源,很多使用的surfaceView控件预览相机,所以就用的是surfaceview。但是我的需求不仅是预览,还需要经常性的拍照,然后在代码里处理图片,而takePicture期间有一段的卡顿时间(拍照的过程)。
后来,硬件工程师让做一个关于双摄的app。要求是能同时在四个地方预览同一个摄像头,并能切换两个摄像头像这样(1代表第一个摄像头,2代表第二个摄像头,画面填充了ImageView控件,有拉伸现象)
发现提到了一个Camera的接口: Camera.PreviewCallback。说明是这样的: Camera.PreviewCallback Called as preview frames are displayed. This callback is invoked on the event thread open(int) was called from.
接口里面有一个回调方法:onPreviewFrame(byte[] data, Camera camera),在预览帧显示的时候调用。
然后在这个方法里实现对拍到的内容data转换成bitmap就行了。然后把这个bitmap显示在需要的ImageView上就行了。目测来看效果还阔以。具体源码角度我没有分析(还没有那个水平),待后期分析。
注意:不能把data简单的转换成btmap,否则得到的bitmap为null。这是因为data格式是YUV的,需要进行数据转换。
下面的代码是另起一个类,实现PreviewCallback接口:只需要看这部分代码就行
public class CameraPreviewCallback implements Camera.PreviewCallback {
private ImageView imageView;
public CameraPreviewCallback(ImageView imageView) {
this.imageView = imageView;
}
@Override
public void onPreviewFrame(byte[] data, Camera camera) {
//格式的转换
Camera.Size previewSize = camera.getParameters().getPreviewSize();
YuvImage image = new YuvImage(data, ImageFormat.NV21, previewSize.width, previewSize.height, null);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
image.compressToJpeg(new Rect(0, 0, previewSize.width, previewSize.height), 80, stream);
Constant.FACE_BITMAP = BitmapFactory.decodeByteArray(stream.toByteArray(), 0, stream.size());
//在指定的imageView上显示预览的图片
imageView.setImageBitmap(Constant.FACE_BITMAP);
}
}
MainActivity代码:主要是不需要写surfaceHolder等,只设置了下预览回调。
(相机预览只是实现了。但相机的正确处理还没有完全弄明白,可能写的有很多不足的地方)
public class MainActivity extends AppCompatActivity implements View.OnClickListener {
private ImageView center = null;
private ImageView right;
private ImageView left;
private ImageView up;
private ImageView down;
// byte[] buffer=new byte[];
private Boolean isPause = false;
private Boolean isSwitch = false;
private Camera mCamera1;
private Camera mCamera2;
private TextView tip_left; //切换摄像头
private TextView tip_right; //暂停/开始预览
private long firstTime = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
findViews();
setListener();
init();
//initSurface();
Log.e("*************", "onCreate调用了");
}
@Override
protected void onStart() {
Log.e("*************", "onStart调用了");
super.onStart();
}
private void setListener() {
tip_left.setOnClickListener(this);
tip_right.setOnClickListener(this);
}
private void findViews() {
center = findViewById(R.id.center);
left = findViewById(R.id.left);
up = findViewById(R.id.up);
down = findViewById(R.id.down);
right = findViewById(R.id.right);
tip_left = findViewById(R.id.tip_left);
tip_right = findViewById(R.id.tip_right);
}
//拍照出错的回调
Camera.ErrorCallback callback = new Camera.ErrorCallback() {
@Override
public void onError(int i, Camera camera) {
switch (i) {
case Camera.CAMERA_ERROR_SERVER_DIED:
Log.d("SERVER_DIED", "CAMERA_ERROR_SERVER_DIED");
break;
case Camera.CAMERA_ERROR_UNKNOWN:
Log.d("UNKNOWN", "CAMERA_ERROR_UNKNOWN");
break;
}
}
};
private void init() {
initCamera(); //开启摄像头
mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
mCamera1.setErrorCallback(callback);
mCamera2.setErrorCallback(callback);
//设置第一次打开app时,中间的ImageView预览摄像头1的内容
}
//初始化摄像头
private void initCamera() {
//如果存在摄像头
if (checkCameraHardware(getApplicationContext())) {
//获取摄像头(首选前置,无前置选后置)
try {
if (openFacingFrontCamera()) {
Log.e("Demo", "openCameraSuccess");
} else {
Log.e("Demo", "openCameraFailed");
}
} catch (Exception e) {
Log.e("Demo", "autoFocus/openFacingFrontCamera函数异常:");
}
}
}
//也有用open(id)打开摄像头的,这里只是参考下。
//得到后置摄像头
private boolean openFacingFrontCamera() {
//尝试开启前置摄像头,因为我用的是两个后摄。下面这个for其实没有执行
Camera.CameraInfo cameraInfo = new Camera.CameraInfo();
for (int camIdx = 0, cameraCount = Camera.getNumberOfCameras(); camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
try {
if (camIdx == 0)
mCamera1 = Camera.open(camIdx);
//这两个摄像头是后置摄像头
if (camIdx == 1)
mCamera2 = Camera.open(camIdx);
} catch (RuntimeException e) {
return false;
}
}
}
//如果开启前置失败(无前置)则开启后置
if (mCamera1 == null) {
for (int camIdx = 0, cameraCount = Camera.getNumberOfCameras(); camIdx < cameraCount; camIdx++) {
Camera.getCameraInfo(camIdx, cameraInfo);
if (cameraInfo.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
try {
if (camIdx == 0)
mCamera1 = Camera.open(camIdx);
//这两个摄像头是后置摄像头
if (camIdx == 1)
mCamera2 = Camera.open(camIdx);
} catch (RuntimeException e) {
return false;
}
}
}
}
try {
mCamera1.startPreview();
mCamera2.startPreview();
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
//判断是否存在摄像头
private boolean checkCameraHardware(Context context) {
if (context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_CAMERA)) {
// 设备存在摄像头
return true;
} else {
// 设备不存在摄像头
Toast.makeText(context, "未检测到摄像头", Toast.LENGTH_SHORT).show();
return false;
}
}
//对焦并拍照
private void autoFocus() {
mCamera1.startPreview();
//自动对焦
mCamera1.autoFocus(myAutoFocus);
mCamera2.startPreview();
//自动对焦
mCamera2.autoFocus(myAutoFocus);
}
//自动对焦回调函数(空)
private Camera.AutoFocusCallback myAutoFocus = new Camera.AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
}
};
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.tip_left: //控制预览画面展示到不同的控件上
mCamera1.startPreview();
mCamera2.startPreview();
if (!isSwitch) { //isSwitch==false camera1->中间
mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
} else { //isSwitch==true camera1->四周
mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
}
isSwitch = !isSwitch;
//以下if作用是解决:当处于暂停状态时,点击切换画面时,
if (isPause) { //本身画面当处于暂停状态时
tip_right.setText("点击暂停预览");
}
isPause = !isPause;
break;
case R.id.tip_right: //控制预览暂停/开始
if (!isPause) {
tip_right.setText("点击开始预览");
mCamera1.stopPreview();
mCamera2.stopPreview();
} else {
if (isSwitch) {
mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
} else {
mCamera1.setPreviewCallback(new MyPreviewCallback(new ImageView[]{right, left, up, down}));
mCamera2.setPreviewCallback(new MyPreviewCallback(new ImageView[]{center}));
}
tip_right.setText("点击暂停预览");
mCamera1.startPreview();
mCamera2.startPreview();
}
isPause = !isPause;
break;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && event.getAction() == KeyEvent.ACTION_DOWN) {
if (System.currentTimeMillis() - firstTime > 2000) {
Toast.makeText(this, "再按一次退出程序", Toast.LENGTH_SHORT).show();
firstTime = System.currentTimeMillis();
return true;
} else {
if (mCamera1 != null) {
mCamera1.stopPreview();
mCamera1.unlock();
mCamera1.release();
mCamera1 = null;
}
if (mCamera2 != null) {
mCamera2.stopPreview();
mCamera2.unlock();
mCamera2.release();
mCamera2 = null;
}
//finish();
System.exit(0);
}
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onDestroy() {
super.onDestroy();
if (mCamera1 != null) {
mCamera1.stopPreview();
mCamera1.unlock();
mCamera1.release();
mCamera1 = null;
}
if (mCamera2 != null) {
mCamera2.stopPreview();
mCamera2.unlock();
mCamera2.release();
mCamera2 = null;
}
}
}