使用Camera2,讓相機功能更靈活
Camera2介紹
Camera2 API是Android系統(tǒng)中用于訪問相機功能的一套API。它提供了更強大和靈活的相機控制能力,相比之前的Camera API,Camera2 API支持更多的功能和更高的性能。
使用Camera2 API,開發(fā)者可以實現(xiàn)更多的相機功能,如手動對焦、手動曝光、原生RAW圖像捕獲等。同時,Camera2 API也提供了更好的性能和更低的延遲,使得相機應用在性能要求較高的場景下能夠更好地發(fā)揮作用。
在Android系統(tǒng)中,相機功能的實現(xiàn)離不開Camera2 API的支持,因此對于需要使用相機功能的應用開發(fā)者來說,熟悉并掌握Camera2 API是非常重要的。
Camera2主要包括以下幾個重要組件:
- CameraManager:相機管理器,用于檢測和連接可用的相機設備。
- CameraDevice:相機設備,代表一個物理相機設備,可以打開、配置和關(guān)閉相機設備。
- CameraCaptureSession:相機捕獲會話,用于發(fā)送捕獲請求和接收捕獲結(jié)果,可以預覽、拍照、錄像等。
- CameraCharacteristics:相機特性,提供了相機設備的靜態(tài)信息,如支持的參數(shù)、分辨率、對焦模式等。
使用Camera2,開發(fā)者可以更靈活地控制相機的功能和參數(shù),實現(xiàn)更豐富的相機應用程序。
Camera2使用
- 檢查相機權(quán)限:確保在AndroidManifest.xml文件中添加了相機權(quán)限
<uses-permission android:name="android.permission.CAMERA" />
- 設置相機預覽
- 創(chuàng)建一個TextureView用于顯示相機預覽畫面。
- 使用CameraManager獲取相機設備的ID,并打開相機。
- 創(chuàng)建CameraCaptureSession用于預覽。
- 拍照
- 創(chuàng)建一個按鈕用于觸發(fā)拍照操作。
- 當用戶點擊按鈕時,使用CameraCaptureSession進行拍照操作。
- 保存拍攝的照片到指定的文件路徑。
- 處理相機權(quán)限和生命周期
- 在onResume方法中檢查相機權(quán)限,并打開相機。
- 在onPause方法中關(guān)閉相機,釋放相關(guān)資源。
示例代碼:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView
android:id="@+id/textureView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"/>
<Button
android:onClick="takePicture"
android:id="@+id/btnCapture"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:text="拍照"/>
</LinearLayout>
public class CameraActivity extends AppCompatActivity {
private static final int REQUEST_CAMERA_PERMISSION = 200;
private static final int REQUEST_WRITE_EXTERNAL_STORAGE_PERMISSION = 201;
private CameraManager cameraManager;
private String cameraId;
private CameraDevice cameraDevice;
private CameraCaptureSession cameraCaptureSession;
private CaptureRequest.Builder captureRequestBuilder;
private ImageReader imageReader;
private 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 boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
return false;
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
}
};
private final CameraDevice.StateCallback stateCallback = new CameraDevice.StateCallback() {
@Override
public void onOpened(@NonNull CameraDevice camera) {
cameraDevice = camera;
createCameraPreview();
}
@Override
public void onDisconnected(@NonNull CameraDevice camera) {
cameraDevice.close();
}
@Override
public void onError(@NonNull CameraDevice camera, int error) {
cameraDevice.close();
cameraDevice = null;
}
};
private final CameraCaptureSession.StateCallback captureSessionStateCallback = new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
cameraCaptureSession = session;
try {
captureRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE);
cameraCaptureSession.setRepeatingRequest(captureRequestBuilder.build(), null, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_camera);
textureView = findViewById(R.id.textureView);
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
@Override
protected void onResume() {
super.onResume();
if (textureView.isAvailable()) {
openCamera();
} else {
textureView.setSurfaceTextureListener(surfaceTextureListener);
}
}
@Override
protected void onPause() {
closeCamera();
super.onPause();
}
private void openCamera() {
cameraManager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
cameraId = cameraManager.getCameraIdList()[0];
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.CAMERA}, REQUEST_CAMERA_PERMISSION);
return;
}
cameraManager.openCamera(cameraId, stateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void createCameraPreview() {
SurfaceTexture texture = textureView.getSurfaceTexture();
texture.setDefaultBufferSize(textureView.getWidth(), textureView.getHeight());
Surface surface = new Surface(texture);
try {
captureRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
captureRequestBuilder.addTarget(surface);
cameraDevice.createCaptureSession(Collections.singletonList(surface), captureSessionStateCallback, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
private void closeCamera() {
if (cameraCaptureSession != null) {
cameraCaptureSession.close();
cameraCaptureSession = null;
}
if (cameraDevice != null) {
cameraDevice.close();
cameraDevice = null;
}
}
public void takePicture() {
if (cameraDevice == null) {
return;
}
CameraManager manager = (CameraManager) getSystemService(Context.CAMERA_SERVICE);
try {
CameraCharacteristics characteristics = manager.getCameraCharacteristics(cameraDevice.getId());
Size[] jpegSizes = null;
if (characteristics != null) {
jpegSizes = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP).getOutputSizes(ImageFormat.JPEG);
}
int width = 640;
int height = 480;
if (jpegSizes != null && jpegSizes.length > 0) {
width = jpegSizes[0].getWidth();
height = jpegSizes[0].getHeight();
}
ImageReader reader = ImageReader.newInstance(width, height, ImageFormat.JPEG, 1);
List<Surface> outputSurfaces = new ArrayList<>(2);
outputSurfaces.add(reader.getSurface());
outputSurfaces.add(new Surface(textureView.getSurfaceTexture()));
CaptureRequest.Builder captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
captureBuilder.addTarget(reader.getSurface());
captureBuilder.set(CaptureRequest.CONTROL_MODE, CameraMetadata.CONTROL_MODE_AUTO);
int rotation = getWindowManager().getDefaultDisplay().getRotation();
captureBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
File file = new File(getExternalFilesDir(null), "pic.jpg");
ImageReader.OnImageAvailableListener readerListener = new ImageReader.OnImageAvailableListener() {
@Override
public void onImageAvailable(ImageReader reader) {
Image image = null;
try {
image = reader.acquireLatestImage();
ByteBuffer buffer = image.getPlanes()[0].getBuffer();
byte[] bytes = new byte[buffer.capacity()];
buffer.get(bytes);
save(bytes);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (image != null) {
image.close();
}
}
}
private void save(byte[] bytes) throws IOException {
OutputStream output = null;
try {
output = new FileOutputStream(file);
output.write(bytes);
} finally {
if (output != null) {
output.close();
}
}
}
};
reader.setOnImageAvailableListener(readerListener, null);
final CameraCaptureSession.CaptureCallback captureListener = new CameraCaptureSession.CaptureCallback() {
@Override
public void onCaptureCompleted(@NonNull CameraCaptureSession session, @NonNull CaptureRequest request, @NonNull TotalCaptureResult result) {
super.onCaptureCompleted(session, request, result);
Toast.makeText(CameraActivity.this, "Saved:" + file, Toast.LENGTH_SHORT).show();
createCameraPreview();
}
};
cameraDevice.createCaptureSession(outputSurfaces, new CameraCaptureSession.StateCallback() {
@Override
public void onConfigured(@NonNull CameraCaptureSession session) {
try {
session.capture(captureBuilder.build(), captureListener, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
@Override
public void onConfigureFailed(@NonNull CameraCaptureSession session) {
}
}, null);
} catch (CameraAccessException e) {
e.printStackTrace();
}
}
}
在這個示例中,我們首先需要獲取相機權(quán)限和存儲權(quán)限,然后通過CameraManager打開相機,創(chuàng)建預覽界面,并實現(xiàn)拍照功能。在拍照時,我們需要設置圖片的大小、旋轉(zhuǎn)角度等參數(shù),并保存拍攝的照片。
Camera2優(yōu)缺點
優(yōu)點
- 「全面的控制」:Camera2 API提供了更多的相機控制,包括手動對焦、曝光、白平衡等功能,使得開發(fā)者可以更精細地控制相機的行為。
- 「性能優(yōu)化」:Camera2 API在性能上進行了優(yōu)化,能夠更好地利用多核處理器和硬件加速功能,提高相機應用的運行效率。
- 「支持原生RAW格式」:Camera2 API支持原生的RAW圖像捕獲,使得攝影愛好者和專業(yè)攝影師可以更好地處理和編輯照片。
缺點
- 「復雜性」:相比于之前的Camera API,Camera2 API的使用復雜度更高,需要開發(fā)者具備更多的相機知識和經(jīng)驗。
- 「兼容性問題」:部分老舊的設備可能不支持Camera2 API,這會導致應用在這些設備上無法正常運行。
- 「學習曲線」:對于新手開發(fā)者來說,學習并掌握Camera2 API可能需要花費更多的時間和精力。