Android实现Camera2预览和拍照效果
背景介绍
Android平台上的相机应用开发主要依赖于两套接口:Camera API和Camera2 API,Camera API相对简单易用,但功能有限;而Camera2 API功能强大,支持更高级的相机控制,如手动对焦、曝光补偿等,能直接访问RAW图像数据,本文将详细介绍如何使用Camera2 API实现相机预览和拍照效果,以满足开发者对于高度自定义相机功能的需求。
目录
1、[环境配置](#1-环境配置)
2、[布局文件](#2-布局文件)
3、[权限配置](#3-权限配置)
4、[Camera2预览](#4-camera2预览)
创建TextureView容器
监听TextureView状态
5、[Camera2拍照](#5-camera2拍照)
创建CaptureRequest.Builder
设置拍照参数
捕获拍照结果
6、[完整代码示例](#6-完整代码示例)
7、[(#7-
1. 环境配置
确保你的开发环境已安装Android Studio,并且项目的编译SDK版本为21或以上,因为Camera2 API是在API level 21(Lollipop)中引入的。
2. 布局文件
在res/layout/activity_main.xml
文件中定义界面布局,包括一个用于预览的TextureView和一个用于显示拍照结果的ImageView,以及一个触发拍照功能的Button。
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <TextureView android:id="@+id/texture_view_camera2" android:layout_width="match_parent" android:layout_height="300dp" /> <ImageView android:id="@+id/image_view_result" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_below="@id/texture_view_camera2" /> <Button android:id="@+id/button_capture" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@id/image_view_result" android:layout_centerHorizontal="true" android:text="Capture" /> </RelativeLayout>
3. 权限配置
在AndroidManifest.xml
中添加相机和存储权限配置,以确保应用能够访问相机设备和存储空间。
<uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-feature android:name="android.hardware.camera2.full" />
4. Camera2预览
创建TextureView容器
创建一个用于显示相机预览的TextureView。
private TextureView textureView;
在onCreate
方法中初始化TextureView并设置其SurfaceTextureListener。
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); textureView = findViewById(R.id.texture_view_camera2); textureView.setSurfaceTextureListener(surfaceTextureListener); }
监听TextureView状态
创建一个SurfaceTextureListener来监听TextureView的状态变化,当TextureView准备好时,开启相机并开始预览。
private final TextureView.SurfaceTextureListener surfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} @Override public void onSurfaceTextureDestroyed(SurfaceTexture surface) {} @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) {} };
5. Camera2拍照
创建CaptureRequest.Builder
在打开相机后,创建一个CaptureRequest.Builder用于构建拍照请求。
private void createCaptureRequest() { try { final CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); captureBuilder.addTarget(textureSurface); // Use the same AE and AF modes as the preview. captureBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE); setAutoFlash(captureBuilder); // Orientation int rotation = getWindowManager().getDefaultDisplay().getRotation(); captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, (sensorOrientation + rotation) % 360); cameraCaptureSession.stopRepeating(); cameraCaptureSession.abortCaptures(); cameraCaptureSession.setRepeatingRequest(null, null, mCaptureCallback); cameraCaptureSession.capture(captureBuilder.build(), null, mCaptureCallback); } catch (CameraAccessException e) { e.printStackTrace(); } }
设置拍照参数
在拍照前,需要锁定焦点并设置其他拍照参数。
private void lockFocus() { try { // Reset the auto-focus trigger captureBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CaptureRequest.CONTROL_AF_TRIGGER_CANCEL); setAutoFlash(captureBuilder); createCaptureSession(captureBuilder); } catch (CameraAccessException e) { e.printStackTrace(); } }
捕获拍照结果
在拍照完成后,通过回调函数获取拍照结果并进行处理。
private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { private void process(Result result) { switch (result.getResultCode()) { case CameraMetadata.RESULT_SUCCESS: Log.i(TAG, "Success"); break; case CameraMetadata.RESULT_MAX_BYTES_EXCEEDED: Log.e(TAG, "Exceeded max bytes"); break; default: Log.e(TAG, "Unknown result code: " + result.getResultCode()); break; } } };
6. 完整代码示例
以下是一个完整的示例,展示了如何在Android中使用Camera2 API实现相机预览和拍照功能,由于篇幅限制,以下代码仅展示了关键部分,请根据实际需求进行补充和完善。
public class MainActivity extends AppCompatActivity { private TextureView textureView; private Button buttonCapture; private ImageView imageViewResult; private CameraDevice cameraDevice; private CameraCaptureSession cameraCaptureSession; private CaptureRequest.Builder captureBuilder; private Size imageDimension; private static final SparseIntArray ORIENTATIONS = new SparseIntArray(); private static final int REQUEST_CAMERA_PERMISSION = 200; private static final String TAG = "AndroidCameraApi"; private static final int STATE_PREVIEW = 0; private static final int STATE_WAITING_LOCK = 1; private static final int STATE_WAITING_PRE_CAPTURE = 2; private static final int STATE_WAITING_NON_PRE_CAPTURE = 3; private static final int STATE_PICTURE_TAKEN = 4; private static final int MAX_WIDTH = 1920; private static final int MAX_HEIGHT = 1080; private static final int NATIVE_ASPECT_RATIO = AspectRatio.RATIO_16_9; private int mState = STATE_PREVIEW; private Semaphore mCameraOpenCloseLock = new Semaphore(1); private boolean mFlashSupported = false; private int mSensorOrientation; private CameraCharacteristics mCharacteristics; private CameraManager mCameraManager; private String mCameraId; private HandlerThread mBackgroundThread; private Handler mBackgroundHandler; private int mImageFormat = ImageFormat.JPEG; private int mJPEGQuality = 100; private long mLastClickTime = 0; private static final int MSG_AUTO_FOCUS = 1; private AutoFocusCallback mAutoFocusCallback = new AutoFocusCallback() { @Override public void onAutoFocus(boolean success, CameraCaptureSession cameraCaptureSession) { mState = success ? STATE_WAITING_PRE_CAPTURE : STATE_WAIT_NON_PRE_CAPTURE; if (!success) { enableCapture(false); // Retry after some delay. } else { lockFocus(); // Start capturing images. } } }; private TextureView.SurfaceTextureListener mSurfaceTextureListener = new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { openCamera(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { } @Override public void onSurfaceTextureDestroyed(SurfaceTexture surface) { } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { } }; private final CameraDevice.StateCallback mStateCallback = new CameraDevice.StateCallback() { @Override public void onOpened(@NonNull CameraDevice camera) { ... } @Override public void onDisconnected(@NonNull CameraDevice camera) { ... } @Override public void onError(@NonNull CameraDevice camera, int error) { ... } }; private final CameraCaptureSession.StateCallback mSessionStateCallback = new CameraCaptureSession.StateCallback() { ... }; private final CameraDevice.StateCallback mSessionReadyCallback = new CameraDevice.StateCallback() { ... }; private final CameraCaptureSession.CaptureCallback mCaptureCallback = new CameraCaptureSession.CaptureCallback() { ... }; private final CameraCaptureSession.StateCallback mPreviewStateCallback = new CameraCaptureSession.StateCallback() { ... }; private final CameraCaptureSession.CaptureCallback mPreviewCaptureCallback = new CameraCaptureSession.CaptureCallback() { ... }; private void createCameraPreviewSession() { ... } private void openCamera() { ... } private void unlockFocus() { ... } private void lockFocus() { ... } private void setAutoFlash(CaptureRequest.Builder requestBuilder) { ... } private void createCameraPreviewSession() throws CameraAccessException { ... } private void closeCamera() { ... } private void startBackgroundThread() { ... } private void stopBackgroundThread() { ... } private void takePicture() { ... } @Override protected void onResume() { ... } @Override protected void onPause() { ... } @Override protected void onDestroy() { ... } @Override public boolean onKeyDown(int keyCode, int event) { ... } }