Android Academy · Урок

Отображение предварительного просмотра с камеры

Подключите предварительный просмотр в реальном времени

Урок 2 из 413 шагов

«Отображение предварительного просмотра с камеры» — бесплатный урок Android Academy на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения Android Academy, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс Android Academy содержит 4 уроков всего.

Цель: предварительный просмотр в реальном времени

Первое, что нужно почти каждому приложению с камерой, — это предварительный просмотр в реальном времени, то есть изображение того, что видит камера, без задержки. В этом уроке вы создадите экран Compose, отображающий изображение с камеры на весь экран.

Вы объедините три компонента: PreviewView, сценарий использования Preview и привязанный ProcessCameraProvider.

Запрос разрешения на использование камеры

Прежде чем показывать предварительный просмотр, необходимо получить разрешение пользователя. В Compose библиотека Accompanist (или официальный API разрешений) упрощает эту задачу с помощью rememberPermissionState.

Запускайте камеру только после того, как разрешение получено: granted.

@OptIn(ExperimentalPermissionsApi::class)
@Composable
fun CameraScreen() {
    val permission = rememberPermissionState(Manifest.permission.CAMERA)

    LaunchedEffect(Unit) { permission.launchPermissionRequest() }

    if (permission.status.isGranted) {
        CameraPreview()
    } else {
        Text("Camera permission is required.")
    }
}

Встраивание PreviewView в Compose

PreviewView — это классическое Android-представление View, поэтому разместите его внутри Compose с помощью AndroidView. Лямбда factory один раз создаёт представление и предоставляет ссылку на него для настройки.

Сохраните созданное представление, чтобы подключить к нему поверхность камеры.

@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
    AndroidView(
        modifier = modifier.fillMaxSize(),
        factory = { context ->
            PreviewView(context).apply {
                scaleType = PreviewView.ScaleType.FILL_CENTER
            }
        }
    )
}

Получение LifecycleOwner

Для привязки камеры нужен LifecycleOwner. В Compose текущий объект можно получить с помощью LocalLifecycleOwner.current, а контекст — с помощью LocalContext.current.

Оба объекта нужны для настройки поставщика камеры и привязки сценариев использования.

val context = LocalContext.current
val lifecycleOwner = LocalLifecycleOwner.current

Создание сценария использования Preview

Сценарий использования Preview передаёт кадры на поверхность. Создав его, подключите его к PreviewView, вызвав setSurfaceProvider и передав поставщик поверхности представления.

Именно эта связь позволяет изображению с камеры появиться на экране.

val preview = Preview.Builder().build()
preview.setSurfaceProvider(previewView.surfaceProvider)

Привязка внутри сопрограммы

Поставщик получается асинхронно, поэтому выполняйте эту операцию в сопрограмме. Используйте LaunchedEffect, привязанный к представлению, чтобы код выполнился один раз. Внутри получите поставщик с помощью await, отмените предыдущие привязки, а затем привяжите предварительный просмотр.

LaunchedEffect(previewView) {
    val cameraProvider = ProcessCameraProvider.getInstance(context).await()
    val preview = Preview.Builder().build().also {
        it.setSurfaceProvider(previewView.surfaceProvider)
    }
    cameraProvider.unbindAll()
    cameraProvider.bindToLifecycle(
        lifecycleOwner,
        CameraSelector.DEFAULT_BACK_CAMERA,
        preview
    )
}

Расширение await()

ProcessCameraProvider.getInstance() возвращает объект ListenableFuture. Расширение await() из kotlinx-coroutines-guava приостанавливает выполнение до получения результата, превращая код с обратными вызовами в понятный последовательный код.

Добавьте зависимость org.jetbrains.kotlinx:kotlinx-coroutines-guava, чтобы использовать это расширение.

import kotlinx.coroutines.guava.await

// Without await you'd use a listener:
// future.addListener({ val provider = future.get() }, executor)
// await() makes it a single suspend call instead.

Собираем всё вместе

Ниже приведён полный компонуемый элемент предварительного просмотра. Обратите внимание на последовательность: создайте PreviewView в factory, сохраните ссылку на него, а затем выполните привязку внутри LaunchedEffect.

@Composable
fun CameraPreview(modifier: Modifier = Modifier) {
    val context = LocalContext.current
    val lifecycleOwner = LocalLifecycleOwner.current
    val previewView = remember { PreviewView(context) }

    LaunchedEffect(previewView) {
        val provider = ProcessCameraProvider.getInstance(context).await()
        val preview = Preview.Builder().build().also {
            it.setSurfaceProvider(previewView.surfaceProvider)
        }
        provider.unbindAll()
        provider.bindToLifecycle(
            lifecycleOwner,
            CameraSelector.DEFAULT_BACK_CAMERA,
            preview
        )
    }

    AndroidView(
        factory = { previewView },
        modifier = modifier.fillMaxSize()
    )
}

Типы масштабирования

Соотношение сторон сенсора камеры редко совпадает с соотношением сторон экрана. PreviewView.ScaleType управляет размещением изображения в представлении:

  • FILL_CENTER — заполняет представление, обрезая края (самый распространённый вариант).
  • FIT_CENTER — показывает весь кадр с полями по краям.

Выберите FILL_CENTER для эффекта полноэкранной камеры.

previewView.scaleType = PreviewView.ScaleType.FILL_CENTER
// or
previewView.scaleType = PreviewView.ScaleType.FIT_CENTER

Переключение камер

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

var selector by remember { mutableStateOf(CameraSelector.DEFAULT_BACK_CAMERA) }

LaunchedEffect(selector) {
    val provider = ProcessCameraProvider.getInstance(context).await()
    provider.unbindAll()
    provider.bindToLifecycle(lifecycleOwner, selector, preview)
}

// Flip button toggles 'selector' between front and back.

Распространённые ошибки

Чёрный предварительный просмотр обычно вызывают две ошибки:

  • Забытый вызов setSurfaceProvider — кадрам некуда выводиться.
  • Отсутствие вызова unbindAll() перед повторной привязкой — CameraX выдаёт ошибку, если сценарий использования привязан дважды.

Также помните: предварительный просмотр не запустится, пока разрешение действительно не будет предоставлено.

Быстрая проверка

Проверьте, насколько хорошо вы поняли настройку предварительного просмотра.

Итоги

Вы создали предварительный просмотр камеры в реальном времени в Compose:

  • Запросите разрешение CAMERA до запуска камеры.
  • Разместите PreviewView с помощью AndroidView.
  • Получите ProcessCameraProvider в сопрограмме с помощью await.
  • Создайте Preview, вызовите setSurfaceProvider, unbindAll(), а затем bindToLifecycle.

Далее вы добавите сценарий использования ImageCapture и сделаете настоящую фотографию.

Можно начать бесплатно

Изучай Kotlin с ИИ-репетитором — бесплатно

Пиши и запускай код прямо в браузере, получай мгновенную помощь от ИИ-репетитора 24/7 и продолжи учиться на сайте или в приложении.

Курсы
36
Уроки
152

Часто задаваемые вопросы

Урок «Отображение предварительного просмотра с камеры» бесплатный?

Да — полный текст урока «Отображение предварительного просмотра с камеры» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс Android Academy, подпишись на CoddyKit PRO. Курс Android Academy содержит 4 уроков всего.

Чему я научусь в уроке «Отображение предварительного просмотра с камеры»?

Подключите предварительный просмотр в реальном времени Ты практикуешь Android Academy с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.

Нужен ли мне опыт, чтобы начать Android Academy?

Предыдущий опыт не требуется. Android Academy на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.

Сколько времени занимает урок «Отображение предварительного просмотра с камеры»?

Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.

Можно ли писать и запускать код в этом уроке Android Academy?

Да. Каждый урок Android Academy включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Обзор CameraX
  2. Отображение предварительного просмотра с камеры
  3. Съёмка фотографий
  4. Запись видео
← Назад к Android Academy