Android Studio: уроки на Kotlin. CameraX: работа с камерой

Доступ к камере — одна из самых востребованных функций в мобильных приложениях: сканирование QR-кодов, создание контента или видеозвонки. Kotlin и современная библиотека CameraX позволяют реализовать это без головной боли, связанной с устаревшим Camera API. Сегодня на уроке мы соберём в Android Studio простое приложение для съёмки фотографий: от запроса разрешений до сохранения снимка в галерею. Вы увидите, как буквально за полчаса добавить в проект работающий видоискатель и кнопку спуска.


CameraX: что это и почему именно она

Google представила CameraX как часть Android Jetpack, чтобы унифицировать работу с камерой на устройствах от разных производителей. Библиотека автоматически выбирает оптимальные настройки под конкретный телефон, поддерживает режимы Preview (видоискатель), ImageCapture (фото) и VideoCapture (видео), а также отлично дружит с жизненным циклом компонентов. Раньше приходилось писать сотни строк кода для фокусировки и поворота изображения — сейчас всё сводится к нескольким builder-методам.


Подключение CameraX и разрешений

Откройте build.gradle.kts уровня модуля и добавьте зависимости. Нам понадобятся три модуля: camera-core, camera-camera2 и camera-lifecycle. Версия 1.3.4 стабильна и работает начиная с API 21.

dependencies {
    implementation("androidx.camera:camera-core:1.3.4")
    implementation("androidx.camera:camera-camera2:1.3.4")
    implementation("androidx.camera:camera-lifecycle:1.3.4")
}

В AndroidManifest.xml добавьте разрешение на использование камеры, а также объявите, что приложению требуется эта функция (без неё оно не установится на устройства без камеры).

<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />

Поскольку доступ к камере — опасное разрешение, его нужно запрашивать во время выполнения. Используем ActivityResultContracts, как мы разбирали в одном из предыдущих уроков.


Создание PreviewView в макете

В activity_main.xml разместите PreviewView — специальный контейнер, который отображает поток с камеры. Добавьте кнопку захвата под ним.

<androidx.camera.view.PreviewView
    android:id="@+id/previewView"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toTopOf="@+id/btnCapture" />

<Button
    android:id="@+id/btnCapture"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:text="Снять"
    app:layout_constraintBottom_toBottomOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintEnd_toEndOf="parent" />

Настройка камеры в Kotlin-коде

В Activity получите экземпляр ProcessCameraProvider — точку входа в CameraX. После получения провайдера свяжите его с жизненным циклом, создайте Preview и ImageCapture, а затем привяжите их к жизненному циклу активности.

val cameraProviderFuture = ProcessCameraProvider.getInstance(this)
cameraProviderFuture.addListener({
    val cameraProvider = cameraProviderFuture.get()

    // Настройка Preview
    val preview = Preview.Builder().build().also {
        it.surfaceProvider = previewView.surfaceProvider
    }

    // Настройка захвата фото
    imageCapture = ImageCapture.Builder().build()

    // Выбор задней камеры
    val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA

    // Привязка к жизненному циклу
    cameraProvider.unbindAll()
    cameraProvider.bindToLifecycle(
        this, cameraSelector, preview, imageCapture
    )
}, ContextCompat.getMainExecutor(this))

Метод bindToLifecycle автоматически останавливает камеру при переходе в фоновый режим и запускает снова при возвращении — всё завязано на жизненный цикл Activity.


Сохранение снимка в галерею

По нажатию кнопки вызываем метод takePicture у объекта ImageCapture. Фотография сохраняется в MediaStore, чтобы она была доступна в галерее. Параллельно можно показать Toast о результате.

btnCapture.setOnClickListener {
    val photoFile = File(externalMediaDirs.first(),
        "${System.currentTimeMillis()}.jpg"
    )
    val outputOptions = ImageCapture.OutputFileOptions.Builder(photoFile).build()
    imageCapture.takePicture(
        outputOptions,
        ContextCompat.getMainExecutor(this),
        object : ImageCapture.OnImageSavedCallback {
            override fun onImageSaved(output: ImageCapture.OutputFileResults) {
                Toast.makeText(this@MainActivity, "Снимок сохранён", Toast.LENGTH_SHORT).show()
            }
            override fun onError(exception: ImageCaptureException) {
                Toast.makeText(this@MainActivity, "Ошибка", Toast.LENGTH_SHORT).show()
            }
        }
    )
}

После выполнения кода вы получите полностью рабочее приложение для фотографирования — видоискатель в реальном времени, кнопка спуска и сохранение в память устройства.


Советы по работе с камерой

  • Проверяйте наличие камеры. Перед вызовом CameraX убедитесь, что устройство вообще имеет камеру, иначе приложение упадёт.
  • Обрабатывайте поворот. По умолчанию CameraX корректно поворачивает изображение, но если вы работаете с пользовательским интерфейсом, учитывайте ориентацию устройства.
  • Тестируйте на реальных устройствах. Эмулятор не всегда точно передаёт поведение камеры — обязательно проверьте работу на живом смартфоне.

Итог


Мы разобрали практический материал по внедрению камеры через CameraX. Вы настроили PreviewView, привязали камеру к жизненному циклу и реализовали сохранение снимков. Теперь вы можете встраивать функцию фото в свои проекты — будь то социальные сети, мессенджеры или инструменты для заметок. Попробуйте добавить переключение между фронтальной и задней камерой, а также режим видеозаписи — это станет отличным продолжением сегодняшнего занятия.