Android使用Camera2打造自定义相机
2017-05-27 23:29
357 查看
从5.0开始(API Level 21),可以完全控制Android设备相机的新api Camera2(android.hardware.Camera2)被引入了进来。在以前的Camera api(android.hardware.Camera)中,对相机的手动控制需要更改系统才能实现,而且api也不友好。不过老的Camera API在5.0上已经过时,在未来的app开发中推荐的是Camera2 API。
Camera2包架构:
Google采用了pipeline(管道)的概念,将Camera Device相机设备和Android Device安卓设备连接起来, Android Device通过管道发送CaptureRequest拍照请求给Camera Device,Camera Device通过管道返回CameraMetadata数据给Android Device,这一切建立在一个叫作CameraCaptureSession的会话中。
基本上我们需要使用的就是这些类啦。其中CameraManager是所有相机设备(CameraDevice)的管理者,要枚举,查询和打开可用的相机设备,就获取CameraManager实例。
单个CameraDevices提供一组静态属性信息,描述硬件设备以及设备的可用设置和输出参数。该信息通过CameraCharacteristics对象提供,可通过getCameraCharacteristics(String)获得。
CameraCharacteristics是CameraDevice的属性描述类,在CameraCharacteristics中可以进行相机设备功能的详细设定(当然了,首先你得确定你的相机设备支持这些功能才行)。
要从相机设备捕获或流式传输图像,应用程序必须首先使用createCaptureSession(List,CameraCaptureSession.StateCallback,Handler)与相机设备一起使用一组输出Surfaces创建摄像机捕获会话。每个Surface必须预先配置适当的大小和格式(如果适用)以匹配相机设备可用的大小和格式。目标Surface可以从各种类获得。
CameraCaptureSession:这是一个非常重要的API,当程序需要预览、拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。
通常,相机预览图像将发送到SurfaceView或TextureView(通过其SurfaceTexture)。
然后,应用程序需要构建一个CaptureRequest,它定义了相机设备捕获单个映像所需的所有捕获参数。该请求还列出了哪些配置的输出表面应该用作此捕获的目标。 CameraDevice具有用于为给定用例创建请求构建器的工厂方法,针对应用程序正在运行的Android设备进行了优化。
CameraRequest和CameraRequest.Builder:当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象。
一旦请求被建立,它可以交给主动捕获会话进行单次捕获或无休止地重复使用。处理请求后,相机设备将产生一个TotalCaptureResult对象,该对象包含有关拍摄时相机设备状态的信息以及使用的最终设置。如果需要舍入或解决矛盾的参数,这些请求可能会有所不同。相机设备还会将图像数据帧发送到请求中包括的每个输出表面。这些相对于输出CaptureResult是异步产生的,有时候稍后会产生。
类图中有着三个重要的callback,其中CameraCaptureSession.CaptureCallback将处理预览和拍照图片的工作,需要重点对待。
这两幅对Camera2接口使用的流程介绍我们综合起来看会有更深的理解。
可以看出调用openCamera方法后会回调CameraDevice.StateCallback这个方法,在该方法里重写onOpened函数。
在onOpened方法中调用createCaptureSession,该方法又回调CameraCaptureSession.StateCallback方法。
在CameraCaptureSession.StateCallback中重写onConfigured方法,设置setRepeatingRequest方法(也就是开启预览)。
setRepeatingRequest又会回调 CameraCaptureSession.CaptureCallback方法。
重写CameraCaptureSession.CaptureCallback中的onCaptureCompleted方法,result就是未经过处理的元数据了。
顺便提一下CameraCaptureSession.CaptureCallback中的onCaptureProgressed方法很明显是在Capture过程中的,也就是在onCaptureCompleted之前,所以,在这之前想对图像干什么就看你的了,像美颜等操作就可以在这个方法中实现了。
可以看出Camera2相机使用的逻辑还是比较简单的,其实就是3个Callback函数的回调,先说一下:setRepeatingRequest和capture方法其实都是向相机设备发送获取图像的请求,但是capture就获取那么一次,而setRepeatingRequest就是不停的获取图像数据,所以呢,使用capture就想拍照一样,图像就停在那里了,但是setRepeatingRequest一直在发送和获取,所以需要连拍的时候就调用它,然后在onCaptureCompleted中保存图像就行了。(注意了,图像的预览也是用的setRepeatingRequest,只是你不处理数据就行了)。
通过上面对Camera2的API的分析,我们可以知道控制拍照的大致步骤为:
调用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)方法打开指定摄像头。该方法的第一个参数代表要打开的摄像头ID;第二个参数用于监听摄像头的状态;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
当摄像头被打开之后,程序即可获取CameraDevice—即根据摄像头ID获取了指定摄像头设备,然后调用CameraDevice的createCaptureSession(List outputs, CameraCaptureSession. StateCallback callback,Handler handler)方法来创建CameraCaptureSession。该方法的第一个参数是一个List集合,封装了所有需要从该摄像头获取图片的Surface,第二个参数用于监听CameraCaptureSession的创建过程;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
不管预览还是拍照,程序都调用CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder,该方法支持TEMPLATE_PREVIEW(预览)、TEMPLATE_RECORD(拍摄视频)、TEMPLATE_STILL_CAPTURE(拍照)等参数。
通过第3步所调用方法返回的CaptureRequest.Builder设置拍照的各种参数,比如对焦模式、曝光模式等。
调用CaptureRequest.Builder的build()方法即可得到CaptureRequest对象,接下来程序可通过CameraCaptureSession的setRepeatingRequest()方法开始预览,或调用capture()方法拍照。
既然只是示例,我们的布局就简单一些就好,就下来我们先为TextureView设置好它的回调:
我们这个案例主要是为了介绍如何用Camera2实现拍照,所以关于尺寸大小适配的处理就不多做了,所以我们就在onSurfaceTextureSizeChanged()中设置并打开Camera。
我们这里就启用后置摄像头,setupCamera()我们就是设置图像尺寸并获得摄像头ID,方便我们在openCamera()中使用。
这样我们算是完成了第一步,按照流程图接下来就是启用我们设备的回调开始预览:
mCameraDevice是我设置的CameraDevice对象,现在给它初始化,我们知道CameraDevice相当于旧的Camera,所以我们就得到了这个摄像头。
这个方法就是我们实现预览的关键,我们设置好了Surface就把它与CaptureRequestBuilder对象关联,然后就是设置会话开始捕获画面。
最后的回调CameraCaptureSession.CaptureCallback就给我们设置预览完成的逻辑处理:
这样就创建好了,但是要注意的是因为Camera2没有onPictureTaken()方法,所以我们不能直接获得图像数据,这里我们要用的是ImageReader:
在处理ImageReader我们可以用handler来做:
这个Run()方法里做的就是把从Image中获得的帧数据输出到指定的文件里,文件名我们用当前时间来生成。
这样我们就做好所有的拍照前的设置了,现在只要处理点击按钮时进行拍照即可。
这里要注意的是给ImageReader的surface的设置必须放在拍照这里,否则再预览的时候就会不断的执行handler,将图像保存下来。
Camera2还有很多的功能,谷歌在给我们提供强大类的时候也让我们的学习量增大了,所以大家不要认为基本了解Camera2的工作流程就是掌握了Camera2,只有能将其运用到我们的开发中去才算掌握了,所以如果你是看到这篇博客才了解了Camera2,那这只是你的第一步而已,让我们彼此共勉,最后附上GitHub的开源项目。
结束语:本文仅用来学习记录,参考查阅。
1、Camera2介绍
在Camera类中我们多是使用这个类的对象去调用方法,而Camera2则是使用多个类去设置,功能更加强大。Camera2包架构:
Google采用了pipeline(管道)的概念,将Camera Device相机设备和Android Device安卓设备连接起来, Android Device通过管道发送CaptureRequest拍照请求给Camera Device,Camera Device通过管道返回CameraMetadata数据给Android Device,这一切建立在一个叫作CameraCaptureSession的会话中。
基本上我们需要使用的就是这些类啦。其中CameraManager是所有相机设备(CameraDevice)的管理者,要枚举,查询和打开可用的相机设备,就获取CameraManager实例。
单个CameraDevices提供一组静态属性信息,描述硬件设备以及设备的可用设置和输出参数。该信息通过CameraCharacteristics对象提供,可通过getCameraCharacteristics(String)获得。
CameraCharacteristics是CameraDevice的属性描述类,在CameraCharacteristics中可以进行相机设备功能的详细设定(当然了,首先你得确定你的相机设备支持这些功能才行)。
要从相机设备捕获或流式传输图像,应用程序必须首先使用createCaptureSession(List,CameraCaptureSession.StateCallback,Handler)与相机设备一起使用一组输出Surfaces创建摄像机捕获会话。每个Surface必须预先配置适当的大小和格式(如果适用)以匹配相机设备可用的大小和格式。目标Surface可以从各种类获得。
CameraCaptureSession:这是一个非常重要的API,当程序需要预览、拍照时,都需要先通过该类的实例创建Session。而且不管预览还是拍照,也都是由该对象的方法进行控制的,其中控制预览的方法为setRepeatingRequest();控制拍照的方法为capture()。
通常,相机预览图像将发送到SurfaceView或TextureView(通过其SurfaceTexture)。
然后,应用程序需要构建一个CaptureRequest,它定义了相机设备捕获单个映像所需的所有捕获参数。该请求还列出了哪些配置的输出表面应该用作此捕获的目标。 CameraDevice具有用于为给定用例创建请求构建器的工厂方法,针对应用程序正在运行的Android设备进行了优化。
CameraRequest和CameraRequest.Builder:当程序调用setRepeatingRequest()方法进行预览时,或调用capture()方法进行拍照时,都需要传入CameraRequest参数。CameraRequest代表了一次捕获请求,用于描述捕获图片的各种参数设置,比如对焦模式、曝光模式……总之,程序需要对照片所做的各种控制,都通过CameraRequest参数进行设置。CameraRequest.Builder则负责生成CameraRequest对象。
一旦请求被建立,它可以交给主动捕获会话进行单次捕获或无休止地重复使用。处理请求后,相机设备将产生一个TotalCaptureResult对象,该对象包含有关拍摄时相机设备状态的信息以及使用的最终设置。如果需要舍入或解决矛盾的参数,这些请求可能会有所不同。相机设备还会将图像数据帧发送到请求中包括的每个输出表面。这些相对于输出CaptureResult是异步产生的,有时候稍后会产生。
类图中有着三个重要的callback,其中CameraCaptureSession.CaptureCallback将处理预览和拍照图片的工作,需要重点对待。
这两幅对Camera2接口使用的流程介绍我们综合起来看会有更深的理解。
可以看出调用openCamera方法后会回调CameraDevice.StateCallback这个方法,在该方法里重写onOpened函数。
在onOpened方法中调用createCaptureSession,该方法又回调CameraCaptureSession.StateCallback方法。
在CameraCaptureSession.StateCallback中重写onConfigured方法,设置setRepeatingRequest方法(也就是开启预览)。
setRepeatingRequest又会回调 CameraCaptureSession.CaptureCallback方法。
重写CameraCaptureSession.CaptureCallback中的onCaptureCompleted方法,result就是未经过处理的元数据了。
顺便提一下CameraCaptureSession.CaptureCallback中的onCaptureProgressed方法很明显是在Capture过程中的,也就是在onCaptureCompleted之前,所以,在这之前想对图像干什么就看你的了,像美颜等操作就可以在这个方法中实现了。
可以看出Camera2相机使用的逻辑还是比较简单的,其实就是3个Callback函数的回调,先说一下:setRepeatingRequest和capture方法其实都是向相机设备发送获取图像的请求,但是capture就获取那么一次,而setRepeatingRequest就是不停的获取图像数据,所以呢,使用capture就想拍照一样,图像就停在那里了,但是setRepeatingRequest一直在发送和获取,所以需要连拍的时候就调用它,然后在onCaptureCompleted中保存图像就行了。(注意了,图像的预览也是用的setRepeatingRequest,只是你不处理数据就行了)。
通过上面对Camera2的API的分析,我们可以知道控制拍照的大致步骤为:
调用CameraManager的openCamera(String cameraId, CameraDevice.StateCallback callback, Handler handler)方法打开指定摄像头。该方法的第一个参数代表要打开的摄像头ID;第二个参数用于监听摄像头的状态;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
当摄像头被打开之后,程序即可获取CameraDevice—即根据摄像头ID获取了指定摄像头设备,然后调用CameraDevice的createCaptureSession(List outputs, CameraCaptureSession. StateCallback callback,Handler handler)方法来创建CameraCaptureSession。该方法的第一个参数是一个List集合,封装了所有需要从该摄像头获取图片的Surface,第二个参数用于监听CameraCaptureSession的创建过程;第三个参数代表执行callback的Handler,如果程序希望直接在当前线程中执行callback,则可将handler参数设为null。
不管预览还是拍照,程序都调用CameraDevice的createCaptureRequest(int templateType)方法创建CaptureRequest.Builder,该方法支持TEMPLATE_PREVIEW(预览)、TEMPLATE_RECORD(拍摄视频)、TEMPLATE_STILL_CAPTURE(拍照)等参数。
通过第3步所调用方法返回的CaptureRequest.Builder设置拍照的各种参数,比如对焦模式、曝光模式等。
调用CaptureRequest.Builder的build()方法即可得到CaptureRequest对象,接下来程序可通过CameraCaptureSession的setRepeatingRequest()方法开始预览,或调用capture()方法拍照。
2、自定义相机
经过上面的说明,相信大家对Camera2的接口已经有了一定的了解,不是很清楚不要紧,实践出真知,我们就开始上代码啦。<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <TextureView android:id="@+id/textureView" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentStart="true" android:layout_alignParentTop="true" /> <FrameLayout android:id="@+id/control" android:layout_width="match_parent" android:layout_height="112dp" android:layout_alignParentBottom="true" android:layout_alignParentStart="true" android:background="@color/control_background"> <Button android:id="@+id/picture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:text="@string/picture" /> </FrameLayout> </RelativeLayout>
既然只是示例,我们的布局就简单一些就好,就下来我们先为TextureView设置好它的回调:
@Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { setupCamera(); openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { }
我们这个案例主要是为了介绍如何用Camera2实现拍照,所以关于尺寸大小适配的处理就不多做了,所以我们就在onSurfaceTextureSizeChanged()中设置并打开Camera。
private void setupCamera() { //获取摄像头的管理者CameraManager CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); try { //遍历所有摄像头 for (String id : manager.getCameraIdList()) { CameraCharacteristics characteristics = manager.getCameraCharacteristics(id); //默认打开后置摄像头 if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT) continue; //获取StreamConfigurationMap,它是管理摄像头支持的所有输出格式和尺寸 StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); // 对于静态图像捕获,我们使用最大的可用尺寸。 mPreviewSize = Collections.max( Arrays.asList(map.getOutputSizes(ImageFormat.JPEG)), new Comparator<Size>() { @Override public int compare(Size lhs, Size rhs) { return Long.signum(lhs.getWidth() * lhs.getHeight() - rhs.getHeight() * rhs.getWidth()); } }); mCameraId = id; break; } } catch (CameraAccessException e) { e.printStackTrace(); } }
我们这里就启用后置摄像头,setupCamera()我们就是设置图像尺寸并获得摄像头ID,方便我们在openCamera()中使用。
private void openCamera() { //获取摄像头的管理者CameraManager CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE); //检查权限 try { if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) { return; } //打开相机,第一个参数指示打开哪个摄像头,第二个参数stateCallback为相机的状态回调接口,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行 manager.openCamera(mCameraId, stateCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } }
这样我们算是完成了第一步,按照流程图接下来就是启用我们设备的回调开始预览:
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCameraDevice = camera; //开启预览 startPreview(); } @Override public void onDisconnected(CameraDevice camera) { } @Override public void onError(CameraDevice camera, int error) { } };
mCameraDevice是我设置的CameraDevice对象,现在给它初始化,我们知道CameraDevice相当于旧的Camera,所以我们就得到了这个摄像头。
private void startPreview() { SurfaceTexture mSurfaceTexture = mPreviewView.getSurfaceTexture(); //设置TextureView的缓冲区大小 mSurfaceTexture.setDefaultBufferSize(mPreviewSize.getWidth(), mPreviewSize.getHeight()); //获取Surface显示预览数据 Surface mSurface = new Surface(mSurfaceTexture); setupImageReader(); //获取ImageReader的Surface Surface imageReaderSurface = mImageReader.getSurface(); try { //创建CaptureRequestBuilder,TEMPLATE_PREVIEW比表示预览请求 mPreviewBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); //设置Surface作为预览数据的显示界面 mPreviewBuilder.addTarget(mSurface); //创建相机捕获会话,第一个参数是捕获数据的输出Surface列表,第二个参数是CameraCaptureSession的状态回调接口,当它创建好后会回调onConfigured方法,第三个参数用来确定Callback在哪个线程执行,为null的话就在当前线程执行 mCameraDevice.createCaptureSession(Arrays.asList(mSurface, imageReaderSurface), mSessionStateCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } }
这个方法就是我们实现预览的关键,我们设置好了Surface就把它与CaptureRequestBuilder对象关联,然后就是设置会话开始捕获画面。
private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { //创建捕获请求 mCaptureRequest = mPreviewBuilder.build(); mPreviewSession = session; //设置反复捕获数据的请求,这样预览界面就会一直有数据显示 mPreviewSession.setRepeatingRequest(mCaptureRequest, mSessionCaptureCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) { } };
最后的回调CameraCaptureSession.CaptureCallback就给我们设置预览完成的逻辑处理:
private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { super.onCaptureCompleted(session, request, result); //重启预览 restartPreview(); } };
private void restartPreview() { try { //执行setRepeatingRequest方法就行了,注意mCaptureRequest是之前开启预览设置的请求 mPreviewSession.setRepeatingRequest(mCaptureRequest, null, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
这样就创建好了,但是要注意的是因为Camera2没有onPictureTaken()方法,所以我们不能直接获得图像数据,这里我们要用的是ImageReader:
private void setupImageReader() { //前三个参数分别是需要的尺寸和格式,最后一个参数代表每次最多获取几帧数据,本例的2代表ImageReader中最多可以获取两帧图像流 mImageReader = ImageReader.newInstance(mPreviewSize.getWidth(), mPreviewSize.getHeight(), ImageFormat.JPEG, 2); //监听ImageReader的事件,当有图像流数据可用时会回调onImageAvailable方法,它的参数就是预览帧数据,可以对这帧数据进行处理 mImageReader.setOnImageAvailableListener(new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { mHandler.post(new ImageSaver(reader.acquireNextImage())); } }, mHandler); }
在处理ImageReader我们可以用handler来做:
public class ImageSaver implements Runnable { private Image mImage; private File mFile; public ImageSaver(Image image) { this.mImage = image; } @Override public void run() { ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); FileOutputStream output = null; SimpleDateFormat sdf = new SimpleDateFormat( "yyyyMMdd_HHmmss", Locale.US); String fname = "IMG_" + sdf.format(new Date()) + ".jpg"; mFile = new File(getApplication().getExternalFilesDir(null), fname); try { output = new FileOutputStream(mFile); output.write(bytes); } catch (IOException e) { e.printStackTrace(); } finally { mImage.close(); if (null != output) { try { output.close(); } catch (IOException e) { e.printStackTrace(); } } } } }
这个Run()方法里做的就是把从Image中获得的帧数据输出到指定的文件里,文件名我们用当前时间来生成。
这样我们就做好所有的拍照前的设置了,现在只要处理点击按钮时进行拍照即可。
private HandlerThread mThreadHandler; private TextureView mPreviewView; private Handler mHandler = new Handler(); private CaptureRequest.Builder mPreviewBuilder; private Button mButton; private ImageReader mImageReader; private String mCameraId; private Size mPreviewSize; private CameraDevice mCameraDevice; private CaptureRequest mCaptureRequest; private CameraCaptureSession mPreviewSession; private static final SparseIntArray ORIENTATION = new SparseIntArray(); static { ORIENTATION.append(Surface.ROTATION_0, 90); ORIENTATION.append(Surface.ROTATION_90, 0); ORIENTATION.append(Surface.ROTATION_180, 270); ORIENTATION.append(Surface.ROTATION_270, 180); } @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_test); requestCameraPermission(); mThreadHandler = new HandlerThread("CAMERA2"); mThreadHandler.start(); mHandler = new Handler(mThreadHandler.getLooper()); mPreviewView = (TextureView) findViewById(textureView); mPreviewView.setSurfaceTextureListener(this); mButton = (Button) findViewById(R.id.picture); mButton.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { try { //获取屏幕方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); //设置CaptureRequest输出到mImageReader //CaptureRequest添加imageReaderSurface,不加的话就会导致ImageReader的onImageAvailable()方法不会回调 mPreviewBuilder.addTarget(mImageReader.getSurface()); //设置拍照方向 mPreviewBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATION.get(rotation)); //聚焦 mPreviewBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); //停止预览 mPreviewSession.stopRepeating(); //开始拍照,然后回调上面的接口重启预览,因为mPreviewBuilder设置ImageReader作为target,所以会自动回调ImageReader的onImageAvailable()方法保存图片 mPreviewSession.capture(mPreviewBuilder.build(), mSessionCaptureCallback, null); } catch (CameraAccessException e) { e.printStackTrace(); } } }); }
这里要注意的是给ImageReader的surface的设置必须放在拍照这里,否则再预览的时候就会不断的执行handler,将图像保存下来。
Camera2还有很多的功能,谷歌在给我们提供强大类的时候也让我们的学习量增大了,所以大家不要认为基本了解Camera2的工作流程就是掌握了Camera2,只有能将其运用到我们的开发中去才算掌握了,所以如果你是看到这篇博客才了解了Camera2,那这只是你的第一步而已,让我们彼此共勉,最后附上GitHub的开源项目。
结束语:本文仅用来学习记录,参考查阅。
相关文章推荐
- 使用android.hardware.camera2打造新的自定义相机
- Android相机使用(系统相机、自定义相机、大图片处理)
- Android 使用极光推送自定义消息打造个性的消息推送效果
- Android网络框架-Volley实践 使用Volley打造自定义ListView
- Android 使用RecycleView打造自定义日历
- Android Camera2自定义相机
- JS 使用 Android + IOS 相册 + 相机 进行自定义头像 + 限制裁剪后图片尺寸 + base64 string 与 图片互相转换
- Android从零开搞系列:自定义View(11)使用ViewPager打造轮播广告条
- Android 用 camera2 API 自定义相机
- Android 使用自定义Dialog打造ActionSheet菜单
- Android相机的基本使用与自定义相机
- Android 用 camera2 API 自定义相机
- Android 用 camera2 API 自定义相机
- Android 使用自定义Dialog打造ActionSheet菜单
- Android 用 camera2 API 自定义相机
- Android Camera2 自定义相机
- Android网络框架-Volley实践 使用Volley打造自定义ListView
- [置顶] android 自定义相机Camera2
- Android 使用自定义Dialog打造ActionSheet菜单
- Android使用自定义AlertDialog(退出提示框)