Как вы делаете снимок с помощью camerax?

#android #kotlin #android-camerax

#Android #kotlin #android-camerax

Вопрос:

Я все еще занимаюсь разработкой Kotlin и Android. Насколько я понял, класс Camera устарел, и Android предлагает вместо этого использовать Camerax, потому что этот высокоуровневый класс не зависит от устройства, и они упростили процесс внедрения камер в приложения.

Я пытался прочитать документацию (https://developer.android.com/training/camerax ) но это написано так плохо, что я едва понял, что они пытаются объяснить. Поэтому я пошел читать весь пример кода, приведенный в самой документации (https://github.com/android/camera-samples/tree/main/CameraXBasic ). Длина кода CameraFragment составляет около 500 строк (игнорируя импорт и различные комментарии).

Действительно ли мне нужно написать 500 строк кода, чтобы просто сделать снимок? Как это можно считать «проще, чем раньше»?

Я имею в виду, что программирование на Android находится на том этапе, когда мне просто нужно написать всего 4 строки кода, чтобы попросить пользователя выбрать изображение из своего хранилища, восстановить его и показать в ImageView. Есть ли ДЕЙСТВИТЕЛЬНО простой способ сделать снимок, или мне действительно нужно остановиться и потерять целый рабочий день, чтобы написать все эти строки кода?

РЕДАКТИРОВАТЬ: возьмите эту страницу документации: https://developer.android.com/training/camerax/architecture#kotlin Все начинается с этого фрагмента кода.

 val preview = Preview.Builder().build()
val viewFinder: PreviewView = findViewById(R.id.previewView)

// The use case is bound to an Android Lifecycle with the following code
val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)
 

cameraProvider появляется из ниоткуда. Что это должно быть? Я узнал, что это ProcessCameraProvider, но как я должен его инициализировать?
Должна ли это быть переменная lateinit или она уже была инициализирована где-то еще?
Потому что, если я попытаюсь написать val cameraProvider = ProcessCameraProvider() , я получу сообщение об ошибке, так что мне делать?
Что такое параметр cameraSelector? Это не определено ранее. Я выяснил, что это селектор для передней или задней камеры, но как я должен это знать, читая эту страницу документации?
Как эта документация могла быть выпущена с такими недостатками?
Как кто-то должен учиться с легкостью?

Комментарии:

1. если вам нужно сделать снимок, вы можете использовать внешнее приложение для камеры, которое сделает это за вас и даст вашему приложению результат. developer.android.com/training/camera/photobasics

2. Разве это не устарело?

3. Я не могу использовать ничего устаревшего, даже если оно работает, поскольку я тренируюсь, чтобы иметь возможность работать над долгосрочным проектом, и поэтому мне нужно избегать устаревших функций. Разве где-нибудь нет хороших письменных примеров о camerax?

4. я думаю, что лучше всего следовать образцу из самого Google. если у вас есть какие-либо вопросы, вы всегда можете задать вопрос здесь. Мы не используем API камеры в ссылке, которую я опубликовал. Мы используем приложение для внешней камеры, которое делает фотографии и возвращает результат. Я не уверен, почему на странице написано «устаревший». Я думаю, что camera api 1 устарел.

5. codelabs.developers.google.com/codelabs/…

Ответ №1:

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

Вы взаимодействуете с камерами устройства с помощью ProcessCameraProvider . Это синглтон, поэтому при первом получении экземпляра if CameraX выполняет его инициализацию.

 val cameraProviderFuture: ListenableFuture<ProcessCameraProvider> = ProcessCameraProvider.getInstance(context)
 

Получение ProcessCameraProvider синглтона возвращает a Future , поскольку может потребоваться асинхронная инициализация библиотеки. В первый раз, когда вы получаете его, это может занять некоторое время (обычно значительно меньше секунды), однако последующие вызовы вернутся немедленно, так как инициализация уже будет выполнена.

Имея ProcessCameraProvider в руках, вы можете начать взаимодействовать с камерами устройства. Вы выбираете, с какой камерой взаимодействовать, используя a CameraSelector , который оборачивает набор фильтров для камеры, которую вы хотите использовать. Как правило, если вы просто пытаетесь использовать основную заднюю или переднюю камеру, вы бы использовали CameraSelector.DEFAULT_BACK_CAMERA или CameraSelector.DEFAULT_FRONT_CAMERA .

Теперь, когда вы определили, какую камеру вы будете использовать, вы создаете варианты использования, которые вам понадобятся. Например, вы хотите сделать снимок, поэтому вы будете ImageCapture использовать вариант использования. Это позволяет сделать один кадр захвата (обычно высокого качества) с помощью камеры и предоставить его либо в виде необработанного буфера, либо сохранить в файле. Чтобы использовать его, вы можете настроить его, если хотите, или вы можете просто позволить CameraX использовать конфигурацию по умолчанию.

 val imageCapture = ImageCapture.Builder().build()
 

В CameraX жизненный цикл камеры управляется a LifecycleOwner , что означает, что когда начинается LifecycleOwner жизненный цикл, камера открывается, а когда она останавливается, камера закрывается. Таким образом, вам нужно будет выбрать жизненный цикл, который будет управлять камерой. Если вы используете an Activity , вы обычно хотите, чтобы камера запускалась при Activity запуске и останавливалась при остановке, поэтому вы бы использовали сам Activity экземпляр в качестве LifecycleOwner , если бы вы использовали a Fragment , вы могли бы использовать его view lifecycle ( Fragment.getViewLifecycleOwner() ) .

Наконец, вам нужно собрать кусочки головоломки вместе.

 processCameraProvider.bindToLifecycle(
   lifecycleOwner,
   cameraSelector,
   imageCapture
)
 

Приложение обычно включает в себя видоискатель, который отображает предварительный просмотр камеры, поэтому вы можете Preview использовать вариант использования и привязать его к ImageCapture варианту использования. Вариант Preview использования позволяет передавать кадры с камеры Surface в . Поскольку настройка Surface и правильное отображение предварительного просмотра на нем могут быть сложными, CameraX предоставляет PreviewView , View который можно использовать с Preview прецедентом для отображения предварительного просмотра камеры. Вы можете ознакомиться с тем, как их использовать, здесь.

 // Just like ImageCapture, you can configure the Preview use case if you'd wish.
val preview = Preview.Builder().build()

// Provide PreviewView's Surface to CameraX. The preview will be drawn on it.
val previewView: PreviewView = findViewById(...)
preview.setSurfaceProvider(previewView.surfaceProvider)

// Bind both the Preview and ImageCapture use cases
processCameraProvider.bindToLifecycle(
   lifecycleOwner,
   cameraSelector,
   imageCapture,
   preview
)
 

Теперь, чтобы на самом деле сделать снимок, вы используете ImageCapture методы on of takePicture . Один предоставляет необработанный буфер JPEG для захваченного изображения, другой сохраняет его в файле, который вы предоставляете (убедитесь, что у вас есть необходимые разрешения на хранение, если они вам нужны).

 imageCapture.takePicture(
   ContextCompat.getMainExecutor(context), // Defines where the callbacks are run
   object : ImageCapture.OnImageCapturedCallback() {
      override fun onCaptureSuccess(imageProxy: ImageProxy) {
         val image: Image = imageProxy.image // Do what you want with the image
         imageProxy.close() // Make sure to close the image
      }

      override fun onError(exception: ImageCaptureException) {
         // Handle exception
      }
   }
)
 
 val imageFile = File("somePath/someName.jpg") // You can store the image in the cache for example using `cacheDir.absolutePath` as a path.
val outputFileOptions = ImageCapture.OutputFileOptions
      .Builder(imageFile)
      .build()
takePicture(
   outputFileOptions,
   CameraXExecutors.mainThreadExecutor(),
   object : ImageCapture.OnImageSavedCallback {
      override fun onImageSaved(outputFileResults: ImageCapture.OutputFileResults) {
                }

      override fun onError(exception: ImageCaptureException) {
      }
   }
)
 

Действительно ли мне нужно написать 500 строк кода, чтобы просто сделать снимок?
Как это можно считать «проще, чем раньше»?

CameraXBasic не такой «базовый», как можно предположить по его названию x) Это более полный пример 3 вариантов использования CameraX. Несмотря CameraFragment на то, что он длинный, он хорошо объясняет вещи, чтобы сделать его более доступным для всех.

CameraX «проще, чем раньше», прежде чем ссылаться в основном на Camera2, с которым, по крайней мере, было немного сложнее начать. CameraX предоставляет более удобный для разработчиков API с его подходом к использованию вариантов использования. Он также обрабатывает совместимость, которая раньше была большой проблемой. Обеспечить надежную работу приложения камеры на большинстве существующих устройств Android очень сложно.

Комментарии:

1. Хотел бы добавить, что Camera2 бесполезен, поскольку он не поддерживается на некоторых устройствах. AFAIK CameraX пытается обработать такие несоответствия, возвращаясь к Camera1. API в любом случае дерьмо.

2. @AntonMalyshev На самом деле это не так, CameraX полностью построен на Camera2, у него нет реализации Camera1. AFAIK, все устройства Android (включая не только телефоны, но и планшеты, некоторые POS-устройства и т. Д.), Которые поддерживают Android Lollipop (уровень API 21), Поддерживают Camera2.

3. Это должно быть указано в официальной документации! Яснее, чем кто-либо когда-либо мог быть.