CameraX для Kotlin: полное руководство с примерами

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

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                    

Рекомендаций