<think>我们使用Camera2 API来实现对焦和拍照功能。主要步骤包括:
1. 初始化相机:获取CameraManager,打开相机设备,创建预览会话。
2. 设置对焦:通过CaptureRequest设置对焦模式,并触发自动对焦。
3. 拍照:创建CaptureRequest.Builder用于拍照,设置输出Surface,然后捕获静态图像。
注意:我们需要在合适的时机(例如预览会话建立后)触发自动对焦,并在用户触发拍照时捕获图像。
以下是Kotlin示例代码的关键步骤:
### 1. 定义必要的变量
```kotlin
private lateinit var cameraManager: CameraManager
private lateinit var cameraId: String
private lateinit var cameraDevice: CameraDevice
private lateinit var captureSession: CameraCaptureSession
private lateinit var previewRequestBuilder: CaptureRequest.Builder
```
### 2. 打开相机
```kotlin
cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
// 这里我们选择后置摄像头(根据需求调整)
cameraId = cameraManager.cameraIdList.first {
val characteristics = cameraManager.getCameraCharacteristics(it)
characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK
}
// 打开相机
cameraManager.openCamera(cameraId, object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) {
cameraDevice = device
// 创建预览会话
createPreviewSession()
}
override fun onDisconnected(device: CameraDevice) {
device.close()
}
override fun onError(device: CameraDevice, error: Int) {
device.close()
}
}, null)
```
### 3. 创建预览会话
在创建预览会话时,我们需要设置一个用于预览的Surface(例如TextureView的SurfaceTexture)并构建预览请求。
```kotlin
private fun createPreviewSession() {
val textureView: TextureView = ... // 你的TextureView
val surfaceTexture = textureView.surfaceTexture
surfaceTexture.setDefaultBufferSize(previewSize.width, previewSize.height)
val previewSurface = Surface(surfaceTexture)
// 创建用于预览的请求
previewRequestBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)
previewRequestBuilder.addTarget(previewSurface)
// 创建CaptureSession,这里我们使用预览Surface和用于保存照片的ImageReader的Surface
cameraDevice.createCaptureSession(
listOf(previewSurface, imageReader.surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
captureSession = session
// 设置自动对焦模式
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
// 设置会话以重复发送预览请求
captureSession.setRepeatingRequest(previewRequestBuilder.build(), null, null)
}
override fun onConfigureFailed(session: CameraCaptureSession) {
// 处理失败
}
}, null
)
}
```
### 4. 设置自动对焦
在上面的预览请求中,我们设置了`CONTROL_AF_MODE_CONTINUOUS_PICTURE`模式,该模式适用于连续自动对焦(适合拍照)。当需要手动触发对焦(例如点击屏幕对焦)时,我们可以触发一次自动对焦操作。
```kotlin
fun triggerFocus() {
// 首先停止重复请求
captureSession.stopRepeating()
// 创建一个新的请求,设置对焦模式为自动对焦并触发
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START)
captureSession.capture(previewRequestBuilder.build(), object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
// 对焦完成后,重新开始预览(并恢复连续对焦模式)
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE)
captureSession.setRepeatingRequest(previewRequestBuilder.build(), null, null)
}
}, null)
}
```
### 5. 拍照
首先,我们需要创建一个ImageReader来接收拍照结果。
```kotlin
private lateinit var imageReader: ImageReader
// 初始化ImageReader
imageReader = ImageReader.newInstance(imageWidth, imageHeight, ImageFormat.JPEG, 1)
imageReader.setOnImageAvailableListener({ reader ->
val image = reader.acquireLatestImage()
// 处理图像数据(例如保存为文件)
saveImage(image)
image.close()
}, null)
```
在用户触发拍照时,我们创建一个拍照请求:
```kotlin
fun takePicture() {
// 停止预览
captureSession.stopRepeating()
// 创建拍照请求
val captureBuilder = cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
addTarget(imageReader.surface)
// 设置拍照参数,例如自动对焦、闪光灯等
set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
// 设置JPEG方向
val rotation = windowManager.defaultDisplay.rotation
val sensorOrientation = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION)
val jpegOrientation = (sensorOrientation + rotation * 90 + 360) % 360
set(CaptureRequest.JPEG_ORIENTATION, jpegOrientation)
}
// 捕获静态图像
captureSession.capture(captureBuilder.build(), object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(session: CameraCaptureSession, request: CaptureRequest, result: TotalCaptureResult) {
// 拍照完成后,重新开始预览
previewRequestBuilder.set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_IDLE)
captureSession.setRepeatingRequest(previewRequestBuilder.build(), null, null)
}
}, null)
}
```
### 6. 保存图像
在`ImageReader`的回调中,我们可以获取到图像数据并保存。
```kotlin
private fun saveImage(image: Image) {
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining())
buffer.get(bytes)
// 保存bytes到文件(注意:需要在后台线程执行文件操作)
// 例如:FileOutputStream(file).use { it.write(bytes) }
}
```
### 注意事项
- 上述代码仅为示例,实际开发中需要处理权限、生命周期(例如在onPause中关闭相机)、线程(使用后台线程执行耗时操作)等问题。
- 需要添加相机权限:`<uses-permission android:name="android.permission.CAMERA" />`
### 引用说明
本实现参考了Android Camera2 API的基本使用流程[^1][^4],并针对对焦和拍照功能进行了具体实现。
§§相关问题§§
1. Camera2 API中如何实现手动对焦?
2. 在Camera2 API中如何处理图像数据(如YUV格式)?
3. Camera2 API与旧版Camera API的主要区别是什么?</think>### Android Camera2 API 对焦和拍照实现(Kotlin)
以下是使用 Camera2 API 实现自动对焦和拍照功能的完整 Kotlin 示例代码:
```kotlin
class CameraActivity : AppCompatActivity() {
// 核心组件
private lateinit var cameraManager: CameraManager
private lateinit var cameraId: String
private var cameraDevice: CameraDevice? = null
private var captureSession: CameraCaptureSession? = null
// UI 组件
private lateinit var textureView: TextureView
private lateinit var captureButton: Button
// 图像处理
private lateinit var imageReader: ImageReader
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
textureView = findViewById(R.id.texture_view)
captureButton = findViewById(R.id.capture_button)
cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
// 初始化相机
setupCamera()
// 拍照按钮点击事件
captureButton.setOnClickListener { captureStillImage() }
}
/** 1. 初始化相机设置 */
private fun setupCamera() {
try {
// 选择后置摄像头
cameraId = cameraManager.cameraIdList.first {
val characteristics = cameraManager.getCameraCharacteristics(it)
characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_BACK
}
// 检查相机权限
if (ContextCompat.checkSelfPermission(this, Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.CAMERA), REQUEST_CAMERA_PERMISSION)
return
}
// 打开相机
cameraManager.openCamera(cameraId, cameraStateCallback, null)
// 初始化ImageReader用于拍照
val characteristics = cameraManager.getCameraCharacteristics(cameraId)
val map = characteristics.get(CameraCharacteristics.SCALER_STREAM_CONFIGURATION_MAP)
val largestSize = map!!.getOutputSizes(ImageFormat.JPEG).maxByOrNull { it.height * it.width }!!
imageReader = ImageReader.newInstance(largestSize.width, largestSize.height, ImageFormat.JPEG, 2)
imageReader.setOnImageAvailableListener(imageListener, null)
} catch (e: CameraAccessException) {
e.printStackTrace()
}
}
/** 2. 相机设备回调 */
private val cameraStateCallback = object : CameraDevice.StateCallback() {
override fun onOpened(device: CameraDevice) {
cameraDevice = device
createPreviewSession()
}
override fun onDisconnected(device: CameraDevice) {
device.close()
cameraDevice = null
}
override fun onError(device: CameraDevice, error: Int) {
device.close()
cameraDevice = null
}
}
/** 3. 创建预览会话 */
private fun createPreviewSession() {
val texture = textureView.surfaceTexture?.apply {
setDefaultBufferSize(textureView.width, textureView.height)
}
val previewSurface = Surface(texture)
val captureBuilder = cameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)?.apply {
addTarget(previewSurface)
// 设置自动对焦模式
set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
}
cameraDevice?.createCaptureSession(
listOf(previewSurface, imageReader.surface),
object : CameraCaptureSession.StateCallback() {
override fun onConfigured(session: CameraCaptureSession) {
captureSession = session
captureBuilder?.let {
it.set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_CONTINUOUS_PICTURE)
session.setRepeatingRequest(it.build(), null, null)
}
}
override fun onConfigureFailed(session: CameraCaptureSession) {
Toast.makeText(this@CameraActivity, "配置失败", Toast.LENGTH_SHORT).show()
}
},
null
)
}
/** 4. 触发拍照 */
private fun captureStillImage() {
cameraDevice?.let { device ->
val captureBuilder = device.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE).apply {
addTarget(imageReader.surface)
// 设置自动对焦并锁定
set(CaptureRequest.CONTROL_AF_MODE, CaptureRequest.CONTROL_AF_MODE_AUTO)
set(CaptureRequest.CONTROL_AF_TRIGGER, CameraMetadata.CONTROL_AF_TRIGGER_START)
// 设置JPEG方向
val rotation = windowManager.defaultDisplay.rotation
val sensorOrientation = cameraManager
.getCameraCharacteristics(cameraId)
.get(CameraCharacteristics.SENSOR_ORIENTATION)!!
val jpegOrientation = (sensorOrientation + rotation * 90 + 360) % 360
set(CaptureRequest.JPEG_ORIENTATION, jpegOrientation)
}
// 捕获静态图像
captureSession?.capture(captureBuilder.build(), captureCallback, null)
}
}
/** 5. 拍照回调 */
private val captureCallback = object : CameraCaptureSession.CaptureCallback() {
override fun onCaptureCompleted(
session: CameraCaptureSession,
request: CaptureRequest,
result: TotalCaptureResult
) {
super.onCaptureCompleted(session, request, result)
Toast.makeText(this@CameraActivity, "照片已保存", Toast.LENGTH_SHORT).show()
// 拍照后恢复预览
createPreviewSession()
}
}
/** 6. 图像保存处理 */
private val imageListener = ImageReader.OnImageAvailableListener { reader ->
val image = reader.acquireLatestImage()
val buffer = image.planes[0].buffer
val bytes = ByteArray(buffer.remaining()).apply { buffer.get(this) }
// 保存图片到文件(需在后台线程执行)
thread {
val output = File(getExternalFilesDir(null), "${System.currentTimeMillis()}.jpg")
FileOutputStream(output).use { it.write(bytes) }
image.close()
}
}
companion object {
private const val REQUEST_CAMERA_PERMISSION = 100
}
}
```
### 关键实现说明:
1. **自动对焦实现**:
- 预览模式使用 `CONTROL_AF_MODE_CONTINUOUS_PICTURE` 实现连续自动对焦[^1]
- 拍照时切换为 `CONTROL_AF_MODE_AUTO` 并触发 `CONTROL_AF_TRIGGER_START` 锁定焦点[^3]
2. **拍照流程**:
- 使用 `ImageReader` 接收 JPEG 格式图像数据
- 设置正确的 `JPEG_ORIENTATION` 保证图像方向正确[^4]
- 通过 `OnImageAvailableListener` 处理图像数据并保存为文件
3. **生命周期管理**:
- 在 `onPause()` 中关闭相机资源
- 正确处理相机权限请求
- 使用后台线程执行文件保存操作
### 布局文件示例 (res/layout/activity_camera.xml):
```xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextureView
android:id="@+id/texture_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<Button
android:id="@+id/capture_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|center_horizontal"
android:text="拍照"
android:layout_margin="16dp"/>
</FrameLayout>
```
### 所需权限:
```xml
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
```
[^1]: Camera2 API 提供了更精细的相机参数控制,包括对焦模式设置[^1]
[^3]: 通过 `CameraCharacteristics` 获取相机特性,使用 `CaptureRequest` 控制对焦行为[^3]
[^4]: 正确处理图像方向是 Camera2 开发中的关键步骤[^4]