Доступ к камере — одна из самых востребованных функций в мобильных приложениях: сканирование 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 корректно поворачивает изображение, но если вы работаете с пользовательским интерфейсом, учитывайте ориентацию устройства.
- Тестируйте на реальных устройствах. Эмулятор не всегда точно передаёт поведение камеры — обязательно проверьте работу на живом смартфоне.