CameraX для Kotlin: полное руководство с примерами
1. Что это и зачем нужна
CameraX — это библиотека от Google для разработки камер на Android, написанная на Kotlin и Java. Она предоставляет простой и согласованный API для работы с камерой, абстрагируя сложность низкоуровневых Camera2 API. CameraX решает множество проблем, с которыми сталкиваются разработчики: поддержка разных версий Android, работа с различными производителями устройств, обработка жизненного цикла Activity/Fragment и поворотов экрана.
Зачем нужна CameraX? Во-первых, она значительно упрощает разработку: вместо сотен строк кода для настройки камеры, предварительного просмотра и захвата изображений, вы пишете 10-20 строк. Во-вторых, она обеспечивает совместимость с устройствами от Android 5.0 (API 21) и выше. В-третьих, CameraX автоматически обрабатывает сложные сценарии, такие как переключение между камерами, изменение ориентации и сохранение состояния при повороте экрана. В-четвертых, библиотека включает в себя встроенные расширения (Extensions), такие как HDR, ночной режим и портретный режим, которые работают на большинстве устройств без дополнительных настроек.
CameraX идеально подходит для приложений, где требуется быстрая интеграция камеры: мессенджеры, социальные сети, приложения для сканирования QR-кодов, приложения для видеозвонков и простые камеры. Если вам нужно создать профессиональное приложение с полным контролем над каждым кадром (например, для компьютерного зрения или профессиональной видеосъемки), возможно, стоит рассмотреть Camera2, но для 90% задач CameraX — лучший выбор.
2. Установка
CameraX устанавливается через систему сборки Gradle. Для Kotlin-проекта добавьте зависимости в файл build.gradle (Module: app).
Точная команда (Gradle):
dependencies {
// Core CameraX
def camerax_version = "1.3.1"
implementation "androidx.camera:camera-core:${camerax_version}"
implementation "androidx.camera:camera-camera2:${camerax_version}"
implementation "androidx.camera:camera-lifecycle:${camerax_version}"
implementation "androidx.camera:camera-view:${camerax_version}"
// Если нужны расширения (HDR, ночной режим и т.д.)
implementation "androidx.camera:camera-extensions:${camerax_version}"
// Для видео
implementation "androidx.camera:camera-video:${camerax_version}"
}
Также убедитесь, что в build.gradle (Project) указан репозиторий Google:
allprojects {
repositories {
google()
mavenCentral()
}
}
После добавления зависимостей синхронизируйте проект (Sync Now).
3. Быстрый старт — минимальный рабочий пример
Создайте новый Activity или Fragment. В макете (XML) добавьте PreviewView:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.camera.view.PreviewView
android:id="@+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Теперь код Activity на Kotlin:
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.CameraSelector
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.core.content.ContextCompat
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class MainActivity : AppCompatActivity() {
private lateinit var cameraExecutor: ExecutorService
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
cameraExecutor = Executors.newSingleThreadExecutor()
val viewFinder = findViewById<androidx.camera.view.PreviewView>(R.id.viewFinder)
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider = cameraProviderFuture.get()
val preview = Preview.Builder().build().also {
it.setSurfaceProvider(viewFinder.surfaceProvider)
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
try {
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
this,
cameraSelector,
preview
)
} catch (exc: Exception) {
exc.printStackTrace()
}
}, ContextCompat.getMainExecutor(this))
}
override fun onDestroy() {
super.onDestroy()
cameraExecutor.shutdown()
}
}
Не забудьте добавить разрешение на камеру в AndroidManifest.xml:
<uses-permission android:name="android.permission.CAMERA" />
И запросить его в рантайме (для Android 6+). В этом примере мы опустили запрос разрешений для краткости, но в реальном приложении это обязательно.
4. Основные методы/функции/классы
4.1 ProcessCameraProvider
Сигнатура: ProcessCameraProvider.getInstance(context: Context): ListenableFuture<ProcessCameraProvider>
Что делает: Получает экземпляр провайдера камеры, который управляет жизненным циклом камеры и связывает её с LifecycleOwner.
Рабочий пример:
val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
// Используем cameraProvider для привязки камеры
}, ContextCompat.getMainExecutor(this))
4.2 Preview
Сигнатура: Preview.Builder().build(): Preview
Что делает: Создает объект предварительного просмотра, который отображает видеопоток с камеры на экране через PreviewView.
Рабочий пример:
val preview = Preview.Builder()
.setTargetResolution(Size(1920, 1080))
.build()
preview.setSurfaceProvider(viewFinder.surfaceProvider)
4.3 ImageCapture
Сигнатура: ImageCapture.Builder().build(): ImageCapture
Что делает: Позволяет захватывать фотографии с камеры. Поддерживает настройку формата, качества и режима вспышки.
Рабочий пример:
val imageCapture = ImageCapture.Builder()
.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
.build()
// Захват фото
imageCapture.takePicture(
ContextCompat.getMainExecutor(this),
object : ImageCapture.OnImageCapturedCallback() {
override fun onCaptureSuccess(image: ImageProxy) {
// Обработка изображения
image.close()
}
override fun onError(exception: ImageCaptureException) {
exception.printStackTrace()
}
}
)
4.4 ImageAnalysis
Сигнатура: ImageAnalysis.Builder().build(): ImageAnalysis
Что делает: Анализирует каждый кадр видеопотока в реальном времени. Используется для компьютерного зрения, сканирования QR-кодов, распознавания лиц и т.д.
Рабочий пример:
val imageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
imageAnalysis.setAnalyzer(cameraExecutor) { imageProxy ->
val rotationDegrees = imageProxy.imageInfo.rotationDegrees
// Анализируем изображение
imageProxy.close()
}
4.5 CameraSelector
Сигнатура: CameraSelector.DEFAULT_BACK_CAMERA или CameraSelector.DEFAULT_FRONT_CAMERA
Что делает: Выбирает камеру (заднюю или фронтальную) для использования.
Рабочий пример:
val cameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
// Или просто:
val cameraSelector = CameraSelector.DEFAULT_FRONT_CAMERA
4.6 bindToLifecycle
Сигнатура: cameraProvider.bindToLifecycle(lifecycleOwner: LifecycleOwner, cameraSelector: CameraSelector, vararg useCases: UseCase): Camera
Что делает: Привязывает один или несколько UseCase (Preview, ImageCapture, ImageAnalysis, VideoCapture) к жизненному циклу Activity или Fragment. Камера автоматически запускается/останавливается при изменении жизненного цикла.
Рабочий пример:
val camera = cameraProvider.bindToLifecycle(
this, // LifecycleOwner
cameraSelector,
preview,
imageCapture,
imageAnalysis
)
4.7 takePicture (ImageCapture)
Сигнатура: imageCapture.takePicture(outputFileOptions: OutputFileOptions, executor: Executor, callback: OnImageSavedCallback)
Что делает: Захватывает фотографию и сохраняет её в файл. Позволяет указать путь, метаданные и ориентацию.
Рабочий пример:
val photoFile = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.jpg")
val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
imageCapture.takePicture(
outputOptions,
cameraExecutor,
object : ImageCapture.OnImageSavedCallback {
override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
Log.d("CameraX", "Photo saved: ${photoFile.absolutePath}")
}
override fun onError(exception: ImageCaptureException) {
exception.printStackTrace()
}
}
)
4.8 VideoCapture
Сигнатура: VideoCapture.Builder().build(): VideoCapture
Что делает: Захватывает видео с камеры. Требует дополнительных разрешений (RECORD_AUDIO) и настройки.
Рабочий пример:
val videoCapture = VideoCapture.Builder()
.setVideoFrameRate(30)
.build()
// Начало записи
val videoFile = File(externalMediaDirs.first(), "${System.currentTimeMillis()}.mp4")
val outputOptions = VideoCapture.OutputFileOptions.Builder(videoFile).build()
videoCapture.startRecording(outputOptions, cameraExecutor, object : VideoCapture.OnVideoSavedCallback {
override fun onVideoSaved(outputFileResults: VideoCapture.OutputFileResults) {
Log.d("CameraX", "Video saved: ${videoFile.absolutePath}")
}
override fun onError(videoCaptureError: Int, message: String, cause: Throwable?) {
Log.e("CameraX", "Error: $message", cause)
}
})
// Остановка записи
videoCapture.stopRecording()
4.9 CameraInfo
Сигнатура: camera.cameraInfo: CameraInfo
Что делает: Предоставляет информацию о камере: поддерживаемые разрешения, наличие вспышки, угол обзора и т.д.
Рабочий пример:
val cameraInfo = camera.cameraInfo
val flashAvailable = cameraInfo.hasFlashUnit()
val sensorRotation = cameraInfo.sensorRotationDegrees
val supportedResolutions = cameraInfo.getSupportedResolutions(ImageFormat.JPEG)
4.10 CameraControl
Сигнатура: camera.cameraControl: CameraControl
Что делает: Позволяет управлять настройками камеры в реальном времени: зум, вспышка, фокус, экспозиция.
Рабочий пример:
val cameraControl = camera.cameraControl
// Включить вспышку
cameraControl.enableTorch(true)
// Установить зум
cameraControl.setZoomRatio(2.0f)
// Линейный зум (0-1)
cameraControl.setLinearZoom(0.5f)
// Автофокус
cameraControl.startFocusAndMetering(
FocusMeteringAction.Builder(
MeteringPointFactory(viewFinder),
FocusMeteringAction.FLAG_AF
).build()
)
5. Реальный практический пример (100-200 строк кода)
Создадим полноценное приложение-камеру с возможностью захвата фото, переключения между передней и задней камерой, зумом и вспышкой.
import android.Manifest
import android.content.pm.PackageManager
import android.os.Bundle
import android.widget.Button
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.camera.core.*
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import java.io.File
import java.util.concurrent.ExecutorService
import java.util.concurrent.Executors
class CameraActivity : AppCompatActivity() {
private lateinit var viewFinder: PreviewView
private lateinit var cameraExecutor: ExecutorService
private var imageCapture: ImageCapture? = null
private var camera: Camera? = null
private var lensFacing = CameraSelector.LENS_FACING_BACK
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_camera)
viewFinder = findViewById(R.id.viewFinder)
cameraExecutor = Executors.newSingleThreadExecutor()
// Запрос разрешений
if (allPermissionsGranted()) {
start