前言
笔者因为项目需要自定义相机,所以了解了一下 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 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.jb51.net/201704/yuanma/camera2-master_jb51.rar
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,同时也希望多多支持编程小技巧!
总结以上是内存溢出为你收集整理的Android 用 camera2 API 自定义相机全部内容,希望文章能够帮你解决Android 用 camera2 API 自定义相机所遇到的程序开发问题。
如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)