Источник: Nuances of Programming
Отображение превью с камеры — обычный вариант использования у любого приложения для съемки фото и видео. Однако сих пор это было довольно трудно сделать правильно, в основном из-за сложностей, связанных с граничными случаями API camera2 и поведением конкретных устройств. PreviewView, часть библиотеки CameraX Jetpack, упрощает отображение превью камеры, предоставляя удобный разработчикам, последовательный и стабильный API для широкого спектра устройств Android.
Знакомство с PreviewView
PreviewView — это кастомный View, который позволяет отображать ленту камеры. Он был спроектирован, чтобы уменьшить нагрузку по настройке и обработке поверхности превью, которая используется камерой.
Если вам нужно отобразить базовое превью камеры в приложении, рекомендуется использовать PreviewView, так как он:
- Проще в использовании: PreviewView — это View. Он осуществляет всю работу, необходимую для отображения того, что видит камера в вашем макете, управляя поверхностью Surface, применяемой в варианте использования Preview.
- Мало весит: PreviewView фокусируется исключительно на превью. Все его внутренние ресурсы направлены на то, чтобы настроить превью камеры и управлять поверхностью превью во время ее использования. Такое разделение ответственности позволяет коду PreviewView оставаться чистым.
- Исчерпывающий: PreviewView обрабатывает все сложные детали отображения ленты камеры на экране, включая соотношение сторон, масштабирование и вращение. Он также содержит исправления совместимости и обходные пути, которые обеспечивают плавное взаимодействие со множеством различных устройств, разрешений экрана, уровней аппаратной поддержки камеры и конфигураций дисплея, таких как режим разделенного экрана, фиксированная ориентация и мульти-окно свободной формы.
PreviewView — режимы реализации
PreviewView — это подкласс FrameLayout. Для отображения ленты камеры он использует либо SurfaceView, либо TextureView, предоставляет камере готовую поверхность превью, пытается сохранить ее в актуальном состоянии, пока камера использует ее, и при преждевременном освобождении предоставляет новую поверхность, если камера все еще используется.
SurfaceView, как правило, лучше TextureView, когда речь заходит о некоторых ключевых метриках, включающих заряд и время ожидания, поэтому PreviewView пытается использовать SurfaceView по умолчанию. Однако некоторые устройства (в основном устаревшие) аварийно завершают работу, когда поверхность превью освобождается преждевременно. К сожалению, при использовании SurfaceView невозможно контролировать, когда поверхность освобождается, так как это контролируется иерархией View. На таких устройствах PreviewView вынужденно возвращается к использованию TextureView. Вы также должны настроить PreviewView на использование TextureView в тех случаях, когда требуется вращение превью, прозрачность или анимация.
Вы можете в явном виде задать ту реализацию, которую хотите использовать в PreviewView, вызвав PreviewView.setPreferredImplementationMode(ImplementationMode), где ImplementationMode — это либо SURFACE_VIEW, либо TEXTURE_VIEW. PreviewView старается уважать ваш выбор и гарантирует использование указанного режима.
⚠️Перед запуском превью обязательно удостоверьтесь, что установили предпочтительный режим реализации, вызвав Preview.setSurfaceProvider(PreviewView.createSurfaceProvider()).
Ниже показано, как установить предпочтительный режим реализации PreviewView:
PreviewView — превью
PreviewView обрабатывает “винтики и шестеренки” создания SurfaceProvider, который необходим в варианте использования Preview для запуска потока превью. SurfaceProvider подготавливает поверхность, которая будет предоставлена камере для отображения потока превью, и при необходимости заботится о ее воссоздании. Preview.createSurfaceProvider(CameraInfo) принимает нуллифицированный экземпляр CameraInfo. PreviewView использует его наряду с предпочтительным режимом реализации и возможностями камеры, чтобы определить реализацию для внутреннего использования. Если вы передаете null CameraInfo, PreviewView использует реализацию TextureView, так как он не может определить, будет ли выбранная камера работать с SurfaceView.
Как только вы сконструируете вариант использования превью и любые другие необходимые вам варианты использования, свяжите их с LifecycleOwner, используйте CameraInfo привязанной камеры для создания SurfaceProvider, а затем прикрепите к варианту использования Preview для запуска потока превью, вызвав Preview.setSurfaceProvider(SurfaceProvider).
Ниже показано, как прикрепить PreviewView к Preview, чтобы запустить поток предварительного просмотра:
PreviewView — способы масштабирования
PreviewView предоставляет API, который позволяет управлять тем, как должно выглядеть превью и где оно должно располагается в контейнере.
- Параметр How определяет, должно ли превью вмещаться внутрь своего контейнера (FIT) или заполнять (FILL) его.
- Параметр where определяет, должно ли превью находиться в верхнем левом углу (START), в центре (CENTER) или в нижнем правом углу (END) своего контейнера.
Доступные значения способов масштабирования, поддерживаемые PreviewView, представляют собой комбинации из “how” и “where”: FIT_START, FIT_CENTER, FIT_END, FILL_START, FILL_CENTER и FILL_END. Чаще всего используются FIT_CENTER, который эквивалентен леттербоксингу превью, и FILL_CENTER, который центрирует превью в контейнере.
Установить способ масштабирования можно двумя путями.
В XML-макете с помощью атрибута scaleType в PreviewView, как показано в следующем примере:
<androidx.camera.view.PreviewView
android:layout_width="match_parent"
android:layout_height="match_parent"
app:scaleType="fitEnd" />
Программно через вызов PreviewView.setScaleType(ScaleType), как показано ниже:
previewView.setScaleType(ScaleType.FIT_CENTER)
Чтобы узнать текущий способ масштабирования, используемый PreviewView, вызовите PreviewView.getScaleType().
PreviewView — управление камерой
В зависимости от ориентации датчика камеры, поворота устройства, режима отображения и типа шкалы предварительного просмотра PreviewView может масштабировать, поворачивать и преобразовывать превью-кадры, полученные от камеры, чтобы правильно отображать поток превью в пользовательском интерфейсе. Вот почему так важно иметь возможность конвертировать координаты пользовательского интерфейса в координаты датчика камеры. В CameraX такое преобразование выполняется с помощью MeteringPointFactory. PreviewView предоставляет API для его создания: PreviewView.createMeteringPointFactory(cameraSelector), где CameraSelector выступает в качестве камеры, транслирующей превью.
MeteringPointFactory в PreviewView очень удобен, когда нужно реализовать tap-to-focus. Несмотря на то, что в превью камеры автофокус включен по умолчанию (если камера поддерживает его), вы также можете управлять тем, какой объект будет выбран целью фокусировки при нажатии на PreviewView. MeteringPointFactory преобразует координаты объекта фокусировки в координаты датчика камеры, что затем позволяет камере фокусироваться на нужной области.
В следующем примере показано, как реализовать tap-to-focus с помощью тач-слушателя, установленного в PreviewView:
Еще одна распространенная функция превью камеры — это pinch-to-zoom, что позволяет камере приближать и отдалять изображение, когда вы делаете щипок по превью. Чтобы реализовать эту функцию с помощью PreviewView, добавьте тач-слушатель в PreviewView и прикрепите его к слушателю жестов масштабирования. Это позволит перехватывать жесты щипка и соответствующим образом обновлять коэффициент увеличения камеры.
В следующем примере показано, как реализовать pinch-to-zoom с помощью PreviewView.
PreviewView — как он тестируется
PreviewView обеспечивает согласованное поведение камеры на широком диапазоне устройств Android. Это стало возможным благодаря инвестициям CameraX в тестирование PreviewView и других API-интерфейсов в лаборатории автоматизированного тестирования. Эти тесты делятся на две основные категории:
- Модульные тесты, которые проверяют поведение PreviewView в отношении режимов реализации, способов масштабирования и MeteringPointFactory. Они также помогают удостоверится, что PreviewView правильно подстраивает превью, когда это необходимо, например, если изменяется размер его контейнера, если обновляется макет дисплея, или если он прикрепляется (первично или повторно) к Window.
- Интеграционные тесты, которые гарантируют, что PreviewView ведет себя правильно, когда является частью приложения, и соответственно отображает или останавливает поток превью. Эти тесты включают в себя проверку состояния превью во время работы приложения, после его многократного закрытия и повторного открытия, после переключения объектива камеры вперед и назад, а также после уничтожения и перезапуска жизненного цикла приложения. В настоящее время эти тесты в основном охватывают реализацию PreviewView с TextureView, поскольку получение от SurfaceView сигнала о том, когда превью запущено или остановлено, оказалось сложной задачей.
Заключение
Подводя итог:
- PreviewView — это пользовательский View, который упрощает отображение превью камеры.
- PreviewView обрабатывает поверхность предварительного просмотра с помощью SurfaceView по умолчанию, но при необходимости или по запросу может вернуться к использованию TextureView.
- Свяжите другие ваши варианты использования, такие как ImageCapture и ImageAnalysis, с LifecycleOwner, а затем запустите превью камеры, прикрепив SurfaceProvider из PreviewView к связанному варианту использования Preview.
- Управление отображением предварительного просмотра осуществляется в PreviewView путем определения способа масштабирования.
- Реализуйте tap-to-focus в вашем превью, получив MeteringPointFactory из PreviewView.
- Реализуйте pinch-to-zoom в вашем превью, настроив в PreviewView слушатель жестов.
Хотите узнать больше о CameraX? Загляните сюда:
Спасибо за чтение!
Читайте также:
Перевод статьи Husayn Hakeem: Display a camera preview with PreviewView