前言

笔者因为项目需要自定义相机,所以了解了一下 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();
}

这样就完成了自定义相机第一步,源码地址请戳这里。

作者:KenChoi - 极光

原文:Android 用 camera2 API 自定义相机

知乎专栏:极光日报

Android 用 camera2 API 自定义相机的更多相关文章

  1. Android L Camera2 API 使用实例程序汇总

    在网上发现几个使用Camera API2开发的实例程序,总结一下方便后续参考: 1.Camera2 Basic : https://github.com/googlesamples/android-C ...

  2. Android Multimedia框架总结(十四)Camera框架初识及自定义相机案例

    转载请把头部出处链接和尾部二维码一起转载,本文出自逆流的鱼yuiop:http://blog.csdn.net/hejjunlin/article/details/52738492 前言:国庆节告一段 ...

  3. android 自定义相机

    老规矩,先上一下项目地址:GitHub:https://github.com/xiangzhihong/CameraDemo 方式: 调用Camera API 自定义相机 调用系统相机 由于需求不同, ...

  4. Android 5.0 API

    Android 5.0 (LOLLIPOP) 为用户和应用开发者提供了新功能.本文旨在介绍其中最值得关注的新 API. 如果您有已发布的应用,请务必看一看 Android 5.0 行为变更,了解您的应 ...

  5. Android 5.0 API新增和改进

    开始开发 要构建 Android 5.0 版应用,您必须先下载 Android SDK,然后使用 SDK 管理器下载 Android 5.0 SDK 平台和系统映像. 更新您的目标 API 级别 要进 ...

  6. Android Camera API/Camera2 API 相机预览及滤镜、贴纸等处理

    Android Lollipop 添加了Camera2 API,并将原来的Camera API标记为废弃了.相对原来的Camera API来说.Camera2是又一次定义的相机 API,也重构了相机 ...

  7. Android相机使用(系统相机、自定义相机、大图片处理)

    本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理(避免OOM),还有简要提一下有些人Surf ...

  8. Android自定义相机拍照、图片裁剪的实现

    最近项目里面又要加一个拍照搜题的功能,也就是用户对着不会做的题目拍一张照片,将照片的文字使用ocr识别出来,再调用题库搜索接口搜索出来展示给用户,类似于小猿搜题.学霸君等app. 其实Android提 ...

  9. Android调用系统相机、自定义相机、处理大图片

    Android调用系统相机和自定义相机实例 本博文主要是介绍了android上使用相机进行拍照并显示的两种方式,并且由于涉及到要把拍到的照片显示出来,该例子也会涉及到Android加载大图片时候的处理 ...

随机推荐

  1. php 手动搭建环境

    php手动搭建环境有好多种组合,版本号不一致,会导致搭建失败. 我搭建的组合是: php5.6+MySQL5.6+Apache2.4的组合. 一.PHP语言包下载 首先从官网上下载php5.6 htt ...

  2. 前端发展态势 && 前端工作流程个人浅析

    于在未开启cleartype的情况下,一些中文字体在非偶数字号下的显示效果欠佳,所以一般建议使用12.14.16.18.22px等偶数字号.也就 是对某个分辨率选择离它最近的偶数字号.例如:屏幕横向分 ...

  3. 微软.NET年芳15:我在Azure上搭建Photon服务器(C#.NET)

    网上火热的“微软.NET年芳15”文章,我也得写点什么嘛,毕竟我还是现任的微软MVP. 摘录网上的“.NET 15周年”信息如下: 微软的 .NET 框架本周迎来了 15 岁生日..NET 的第一个版 ...

  4. hdoj 1257 DP||贪心

    最少拦截系统 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Subm ...

  5. Selenium 显示等待和隐式等待

    1. 设置显示等待 Java代码: 1 2 3 4 5 public static WebElement WaitForElement(WebDriver driver, String locator ...

  6. Struts2学习笔记④

    刚才看书发现了一个问题,就是ActionSupport和Action接口的区别没搞清楚,弄得我以为我之前的代码写错了.其实ActionSupport已经实现了Action接口了,实际开发中也很少使用A ...

  7. xml与json的原理,区别,优缺点.

    1.定义介绍 (1).XML定义扩展标记语言 (Extensible Markup Language, XML) ,用于标记电子文件使其具有结构性的标记语言,可以用来标记数据.定义数据类型,是一种允许 ...

  8. Raft算法,从学习到忘记

    Raft算法,从学习到忘记 --Raft算法阅读笔记. --Github 概述 说到分布式一致性算法,可能大多数人的第一反应是paxos算法.但是paxos算法一直以来都被认为是难以理解,难以实现.S ...

  9. JavaWeb之Filter过滤器

    原本计划这一篇来总结JSP,由于JSP的内容比较多,又想着晚上跑跑步减减肥,所以今天先介绍Filter以及它的使用举例,这样的话还有些时间可以锻炼锻炼.言归正传,过滤器从字面理解她的话有拦网.过滤的功 ...

  10. js页面跳转常用的几种方式

    第一种: <script language="javascript" type="text/javascript"> window.location ...