首页 > 系统 > Android > 正文

Android 用 camera2 API 自定义相机

2019-12-12 03:11:09
字体:
来源:转载
供稿:网友

前言

笔者因为项目需要自定义相机,所以了解了一下 Android 关于 camera 这块的 API。Android SDK 21(LOLLIPOP) 开始已经弃用了之前的 Camera 类,提供了 camera2 相关 API,目前网上关于 camera2 API 介绍的资料比较少,笔者搜集网上资料,结合自己的实践,在这里做一个总结。

流程

因为 camera2 提供的接口比较多,虽然很灵活,但是也增加了使用的复杂度。首先来大致了解一下调用 camera2 的流程,方便我们理清思路。

要显示相机捕捉的画面,只需要三步:初始化相机,预览,更新预览。也就是上图中左侧的部分。要实现这三步,需要用到的主要接口类和它们的作用步骤如上图右侧部分所示。下面就用代码来详解一下。

案例

首先创建一个相机界面:

activity_camera.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <TextureView  android:id="@+id/camera_texture_view"  android:layout_width="match_parent"  android:layout_height="match_parent" /> <ImageButton  android:id="@+id/capture_ib"  android:layout_width="60dp"  android:layout_height="60dp"  android:layout_marginBottom="10dp"  android:layout_gravity="bottom|center"  android:background="@drawable/send_pres"/></LinearLayout>

界面很简单,只有一个 TexureView 和一个按钮。

接下来在 Activity 中初始化并显示相机捕捉的画面。

首先要解决的一个问题就是画面拉伸的问题。

要解决这个问题,首先要从 TextureView 下手。

CameraActivity.java

mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() {  @Override  public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {  mWidth = width;  mHeight = height;  getCameraId();  openCamera();  }  @Override  public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) {  }  @Override  public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {  return false;  }  @Override  public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {  } });

在 onSurfaceTextureAvailable 中初始化相机。通过 CameraManager 对象 openCamera,这正是流程图中 Init 步骤中的第一步。openCamera 有三个参数,第一个是 String 类型的 cameraId,第二个是 CameraDevice.StateCallback,第三个是 Handler。这里我们要声明一个 StateCallback:

private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice cameraDevice) {  mCameraDevice = cameraDevice;  createCameraPreview(); } @Override public void onDisconnected(CameraDevice cameraDevice) {  mCameraDevice.close();  mCameraDevice = null; } @Override public void onError(CameraDevice cameraDevice, int i) {  mCameraDevice.close();  mCameraDevice = null; } };

可以看到,在 camera 准备完毕之后就可以创建预览界面了。解决画面拉伸的问题就是要为预览界面设置一个合适比例的 SurfaceTexture buffer size。

private void createCameraPreview() { try {  SurfaceTexture texture = mTextureView.getSurfaceTexture();  assert texture != null;  CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(mCameraId);  StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP);  int deviceOrientation = getWindowManager().getDefaultDisplay().getOrientation();  int totalRotation = sensorToDeviceRotation(characteristics, deviceOrientation);  boolean swapRotation = totalRotation == 90 || totalRotation == 270;  int rotatedWidth = mWidth;  int rotatedHeight = mHeight;  if (swapRotation) {  rotatedWidth = mHeight;  rotatedHeight = mWidth;  }  mPreviewSize = getPreferredPreviewSize(map.getOutputSizes(SurfaceTexture.class), rotatedWidth, rotatedHeight);  texture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight());  Log.e("CameraActivity", "OptimalSize width: " + mPreviewSize.getWidth() + " height: " + mPreviewSize.getHeight());  ...

这里根据当前设备及传感器的旋转角度来判断是否交换宽高值,然后通过 CameraCharacteristics 来得到最适合当前大小比例的宽高,然后把这个宽高设置给 SurfaceTexture 。

private Size getPreferredPreviewSize(Size[] sizes, int width, int height) { List<Size> collectorSizes = new ArrayList<>(); for (Size option : sizes) {  if (width > height) {  if (option.getWidth() > width && option.getHeight() > height) {   collectorSizes.add(option);  }  } else {  if (option.getHeight() > width && option.getWidth() > height) {   collectorSizes.add(option);  }  } } if (collectorSizes.size() > 0) {  return Collections.min(collectorSizes, new Comparator<Size>() {  @Override  public int compare(Size s1, Size s2) {   return Long.signum(s1.getWidth() * s1.getHeight() - s2.getWidth() * s2.getHeight());  }  }); } return sizes[0]; }这里 Sizes 是相机返回的支持的分辨率,从我们传递的参数找找到一个最接近的分辨率。接下来就要通过 CaptureRequest.Builder以及 CameraCaptureSession.StateCallback 来创建及更新预览界面:...Surface surface = new Surface(texture);  mBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);  // 设置预览对象  mBuilder.addTarget(surface);  mCameraDevice.createCaptureSession(Arrays.asList(surface), new CameraCaptureSession.StateCallback() {  @Override  public void onConfigured(CameraCaptureSession cameraCaptureSession) {   if (null == mCameraDevice) {   return;   }   mSession = cameraCaptureSession;   mBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);   try {   // 不停地将捕捉的画面更新到 TextureView   mSession.setRepeatingRequest(mBuilder.build(), mSessionCaptureCallback, mBackgroundHandler);   } catch (CameraAccessException e) {   e.printStackTrace();   }  }  @Override  public void onConfigureFailed(CameraCaptureSession cameraCaptureSession) {   Toast.makeText(CameraActivity.this, "Camera configuration change", Toast.LENGTH_SHORT).show();  }  }, null); } catch (CameraAccessException e) {  e.printStackTrace(); }

这样就完成了自定义相机第一步,源码地址请戳这里。下载地址:http://xiazai.VeVB.COm/201704/yuanma/camera2-master_jb51.rar

以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持武林网!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表