Android开发 之 Camera2之拍照
16lz
2021-01-26
Camera2之拍照
Android框架包括支持各种相机和相机的特性 设备,允许您捕获照片和视频在您的应用程序。 本文讨论了 快速、简单的图像和视频捕获方法,并概述了一个先进的方法来创建 为用户自定义相机的经验。1.在清单文件中声明 权限
如果保存照片,录视频还要添加两个权限
相机功能:相机的特性,例如
还有很多如下: android.hardware.camera应用使用设备的后置相机。只有前置相机的设备不会列出该功能,因此如果您的应用可与任何朝向的相机通信,请改用 android.hardware.camera.any 功能。android.hardware.camera.any应用使用设备的其中一个相机或用户为设备连接的外置相机。 如果您的应用不要求相机必须是后置式,请使用此值来替代 android.hardware.camera。android.hardware.camera.autofocus应用使用设备相机支持的自动对焦功能。应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false"。android.hardware.camera.capability.manual_post_processing应用使用设备相机支持的 MANUAL_POST_PROCESSING 功能。您的应用可以通过该功能替换相机的自动白平衡功能。 使用 android.colorCorrection.transform、android.colorCorrection.gains 以及 TRANSFORM_MATRIX 的 android.colorCorrection.mode。android.hardware.camera.capability.manual_sensor应用使用设备相机支持的 MANUAL_SENSOR 功能。该功能隐含对自动曝光锁定 (android.control.aeLock) 的支持,该支持可以让相机的曝光时间和灵敏度一直固定在特定值。android.hardware.camera.capability.raw应用使用设备相机支持的 RAW 功能。该功能暗示设备可以保存 DNG(原始)文件,并且设备的相机提供您的应用直接处理这些原始图像所需的 DNG 相关元数据。android.hardware.camera.external应用与用户为设备连接的外置相机通信。 但该功能不能保证外置相机可供您的应用使用。android.hardware.camera.flash应用使用设备相机支持的闪光功能。应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false"。android.hardware.camera.front应用使用设备的前置相机。应用通过使用该功能暗示其还使用 android.hardware.camera 功能,除非这个父功能在声明时使用了 android:required="false"。android.hardware.camera.level.full应用使用设备的至少一个相机提供的 FULL 级图像捕捉支持。 提供 FULL 支持的相机可提供快速捕捉功能、逐帧控制和手动后期处理控制。
下面看看具体的代码步骤: 1.本文是利用textureview预览视图数据的。在xml文件中定义textureview组件 创建handler线程,创建handler; 给textureview设置监听
mPreviewView = (TextureView) findViewById(R.id.textureview); mThreadHandler = new HandlerThread("CAMERA2"); mThreadHandler.start(); mHandler = new Handler(mThreadHandler.getLooper()); mPreviewView.setSurfaceTextureListener(surfaceTextureListener);
在textureveiw监听中可以看到以下方法;创建时,尺寸改变时,销毁时,更新时。 我们在textureview初始化的时候创建初始化相机功能,这里先要检察权限,没有权限要去申请权限。 private TextureView.SurfaceTextureListener surfaceTextureListener=new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int i, int i1) { checkPermission(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int i, int i1) { } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) { return false; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) { } };
public void checkPermission() { //检查是否有权限 if(ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, 0); }else{ initCamera(); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); switch (requestCode) { case 0: if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { initCamera(); } break; } }
2.初始化相机功能 获得相机设备要通过相机服务CameraManager 通过相机id获得相机的参数类CameraCharactoristics对象,这个对象封装了相机相关的支持动能参数,例如支持的分辨率,摄像头旋转角度 通过cameraManager传入相机的id打开相机,设置回调 public void initCamera() { cameraManager = (CameraManager) getSystemService(CAMERA_SERVICE); try { //获取可用相机设备id列表 String[] CameraIdList = cameraManager.getCameraIdList(); //获得前置摄像头的属性对象。 CameraCharacteristics characteristics = cameraManager.getCameraCharacteristics(CameraIdList[0]); //这可用流配置 相机设备支持; 还包括最低帧持续时间 和每个格式的停滞时间/大小组合。// characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL); //分辨率: StreamConfigurationMap map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP); android.util.Size[] outputSizes = map.getOutputSizes(ImageFormat.JPEG); for(android.util.Size size:outputSizes) { list.add(size.getWidth()+"*"+size.getHeight()); Log.e("fbl",size.getWidth()+"*"+size.getHeight()); } //获取摄像头的旋转角度 mSensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION); ArrayAdapter arr_adapter= new ArrayAdapter(Camera2Activity.this, R.layout.spinner_item_layout, list); arr_adapter.setDropDownViewResource(R.layout.spinner_item_layout); spinner.setAdapter(arr_adapter);// spinner.setSelection(5); //就像这样 cameraManager.openCamera(CameraIdList[0], mCameraDeviceStateCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
在回调中有打开,失去连接,错误方法的回调 在开发相机回调中设置spinner的监听,对于选择不同的分辨率下创建不同尺寸的ImageReader对象,这个对象是封装了拍照数据的类,设置拍照监听回调后保存照片 开始预览,创建捕捉会议 /** * 相机监听,这个回调不是运行在ui线程的,不能更新UI */ private CameraDevice.StateCallback mCameraDeviceStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(CameraDevice camera) { mCaera = camera; mFile = new File(Environment.getExternalStorageDirectory(),"TTT.jpg"); spinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView<?> adapterView, View view, int i, long l) { TextView tv= (TextView) view; String text = (String) tv.getText(); String[] split = text.split("\\*"); imageReader = ImageReader.newInstance(Integer.parseInt(split[1]), Integer.parseInt(split[0]), ImageFormat.JPEG, /*maxImages*/2); imageReader.setOnImageAvailableListener(mOnImageAvailableListener, mHandler); ViewGroup.LayoutParams layoutParams = mPreviewView.getLayoutParams(); layoutParams.width=min; layoutParams.height=Integer.parseInt(split[0])*min/Integer.parseInt(split[1]); mPreviewView.setLayoutParams(layoutParams); try { if(mSession!=null){ mSession.close(); } startPreview(mCaera); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onNothingSelected(AdapterView<?> adapterView) { } }); } @Override public void onDisconnected(CameraDevice camera) {} @Override public void onError(CameraDevice camera, int error) {} };
3.创建捕捉会议,开始预览 获得预览的surface,图片缓存的surface 设置会议监听 /** * 开始预览 * @param camera * @throws CameraAccessException */ private void startPreview(CameraDevice camera) throws CameraAccessException { SurfaceTexture texture = mPreviewView.getSurfaceTexture(); //我们将默认缓冲区的大小配置为我们想要的相机预览的大小。 texture.setDefaultBufferSize(mPreviewView.getWidth(), mPreviewView.getHeight()); surface = new Surface(texture); //我们设置了一个具有输出Surface的CaptureRequest.Builder。 try { mPreviewBuilder = camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); mPreviewBuilder.addTarget(surface); } catch (CameraAccessException e) { e.printStackTrace(); } //创建捕捉会议 camera.createCaptureSession(Arrays.asList(surface,imageReader.getSurface()), mSessionStateCallback, mHandler); }
会议监听中通过会议发送请求,这只请求监听
/** * 会议状态监听 */ private CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() { @Override public void onConfigured(CameraCaptureSession session) { try { //session.capture(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler); mSession = session; //这句是预览的真正代码 session.setRepeatingRequest(mPreviewBuilder.build(), mSessionCaptureCallback, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } } @Override public void onConfigureFailed(CameraCaptureSession session) {} }; private CameraCaptureSession.CaptureCallback mSessionCaptureCallback = new CameraCaptureSession.CaptureCallback() { @Override public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) { } @Override public void onCaptureProgressed(CameraCaptureSession session, CaptureRequest request, CaptureResult partialResult){ } };
4.通过点击事件触发拍照 创建捕捉请求操作就会在ImageReader的surface中获取一张照片,然后之前设置的拍照监听中去处理图片
@Override public void onClick(View view) { switch (view.getId()) { case R.id.bt_cut: takePicture(); break; case R.id.imagebutton: break; } }
public void takePicture(){ try { CaptureRequest.Builder captureRequestBuilder = mCaera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); // 将imageReader的surface作为CaptureRequest.Builder的目标 captureRequestBuilder.addTarget(imageReader.getSurface()); // 自动对焦 captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); // 获取手机方向 int rotation = getWindowManager().getDefaultDisplay().getRotation(); // 根据设备方向计算设置照片的方向 captureRequestBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation)); //拍照 CaptureRequest mCaptureRequest = captureRequestBuilder.build(); mSession.stopRepeating(); mSession.capture(mCaptureRequest, null, mHandler); } catch (CameraAccessException e) { e.printStackTrace(); } }
5.处理保存图片 private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = new ImageReader.OnImageAvailableListener() { @Override public void onImageAvailable(ImageReader reader) { MyToast.makeText(Camera2Activity.this,"拍照成功"); mHandler.post(new ImageSaver(reader.acquireNextImage(), mFile)); } };
/** * 保存图片 */ private static class ImageSaver implements Runnable { /** * The JPEG image */ private final Image mImage; /** * The file we save the image into. */ private final File mFile; public ImageSaver(Image image, File file) { mImage = image; mFile = file; } @Override public void run() { ByteBuffer buffer = mImage.getPlanes()[0].getBuffer(); byte[] bytes = new byte[buffer.remaining()]; buffer.get(bytes); FileOutputStream output = null; 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(); } } } } }
6.关闭摄像头,释放资源 @Override protected void onPause() { closeCamera(); stopBackgroundThread(); super.onPause(); } public void closeCamera() { if(mSession!=null) { mSession.close(); mSession=null; } if (null != mCaera) { mCaera.close();//关掉摄像头 mCaera = null; } } /** * Stops the background thread and its {@link Handler}. */ private void stopBackgroundThread() { mThreadHandler.quitSafely();//线程安全停止 try { mThreadHandler.join(); mThreadHandler = null; mHandler = null; } catch (InterruptedException e) { e.printStackTrace(); } }
总体比5.0以前的相机操作复杂很多,很多回调监听。 本文参考官方提供的demo: https://github.com/googlesamples/android-Camera2Basic
更多相关文章
- Android蓝牙开发中电话音频(HSP,HFP)和媒体音频(A2DP,AVRCP)到底是个什
- Android中使用官方提供好的功能使用说明(比如系统图库获取),也作
- [置顶] 了解Android微信里的WebView是如何实现分享的功能
- Android蓝牙开发中电话音频(HSP,HFP)和媒体音频(A2DP,AVRCP)到底是个什
- Android之使用SoundPool播放一小段音频,实现猜歌的功能
- android 借助AccessibilityService实现模拟点击功能-循环一个列
- Android(安卓)插件框架 xCombine 开发思路简介
- 将你的老旧Android平板或手机改造成服务器
- [置顶] Android(安卓)ListView点击之后保持更换的背景色,实现已读