Мое приложение показывает предварительный просмотр камеры и сохраняет изображения в файлы. И предварительный просмотр, и сохраненные изображения показывают только часть того, что видит камера. Изображение явно обрезано/обрезано. Как настроить захват изображения, чтобы отображалось все поле зрения камеры?
Целевое приложение представляет собой встроенную систему, использующую USB-камеру, но проблема возникает в эмуляторе, когда он настроен для веб-камеры 0 – веб-камеры, к которой я подключился к ноутбуку через USB. Итак, это аналогичная установка. И проблемное поведение возникает как в этой настройке теста/эмулятора, так и в моей встроенной производственной настройке. Итак, я думаю, что проблема в Android, а не в конфигурации встроенной системы.
Я пробовал использовать и Camera2, и CameraX, и результат один и тот же.
Чтобы поделиться здесь, я воспользовался этим руководством, чтобы создать простое приложение для камеры: https://developer.android.com/codelabs/ ... ng-started. Я знаю, что это не маленький пример кода, но это лучшее, что я мог придумать, поскольку я не знаю, какая часть кода важна.
В полученном приложении есть проблема — по крайней мере, у меня. Вот снимок приложения. На нем показана половина меня, когда я знаю, что камера направлена на меня.

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

Это даже не полное представление. Вот снимок с сайта webcamtest:

Я добавил это в действие в надежде, что это заставит представление отображаться полностью:
viewBinding.viewFinder.setScaleType(PreviewView.ScaleType.FIT_CENTER) Но это не имеет никакого эффекта

Мое приложение написано на Java, но начальное приложение — на Kotlin. Проблема возникает на любом языке, поэтому я готов обсудить проблему и решения на любом языке. Если понадобится, переведу на Java.
Вот код активности:
класс MainActivity : AppCompatActivity() { частный lateinit var viewBinding: ActivityMainBinding частная переменная imageCapture: ImageCapture? = ноль частная переменная videoCapture: VideoCapture? = ноль частная запись var: Запись? = ноль частный lateinit var cameraExecutor: ExecutorService переопределить fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) viewBinding = ActivityMainBinding.inflate(layoutInflater) setContentView(viewBinding.root) // Запрашиваем разрешения камеры если (allPermissionsGranted()) { НачатьКамеру() } еще { запрос разрешений () } // Настраиваем слушателей для кнопок фото и видеосъемки viewBinding.imageCaptureButton.setOnClickListener { takePhoto() } viewBinding.videoCaptureButton.setOnClickListener { captureVideo() } var ScaleType = viewBinding.viewFinder.getScaleType(); viewBinding.viewFinder.setScaleType(PreviewView.ScaleType.FIT_CENTER) ScaleType = viewBinding.viewFinder.getScaleType(); cameraExecutor = Executors.newSingleThreadExecutor() } частное развлечение takePhoto() { // Получаем стабильную ссылку на вариант использования изменяемого захвата изображений val imageCapture = imageCapture ?: возврат // Создаем имя с отметкой времени и запись в MediaStore. имя val = SimpleDateFormat(FILENAME_FORMAT, Locale.US) .format(System.currentTimeMillis()) val contentValues = ContentValues().apply { put(MediaStore.MediaColumns.DISPLAY_NAME, имя) put(MediaStore.MediaColumns.MIME_TYPE, "изображение/jpeg") если (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { put(MediaStore.Images.Media.RELATIVE_PATH, «Изображения/CameraX-Image») } } // Создаем объект параметров вывода, который содержит файл + метаданные val outputOptions = ImageCapture.OutputFileOptions .Builder(contentResolver, MediaStore.Images.Media.EXTERNAL_CONTENT_URI, содержаниеЗначения) .строить() // Настраиваем прослушиватель захвата изображения, который срабатывает после того, как фотография была // были приняты imageCapture.takePicture( выходные параметры, ContextCompat.getMainExecutor(это), объект: ImageCapture.OnImageSavedCallback { переопределить fun onError (искл.: ImageCaptureException) { Log.e(TAG, «Не удалось сделать снимок: ${exc.message}», exc) } переопределить веселье onImageSaved(выход: ImageCapture.OutputFileResults){ val msg = "Фото удалось сделать: ${output.savedUri}" Toast.makeText(baseContext, msg, Toast.LENGTH_SHORT).show() Log.d(ТЕГ, сообщение) } } )} личное развлечение captureVideo() {} частное развлечение startCamera() { val cameraProviderFuture = ProcessCameraProvider.getInstance(this) cameraProviderFuture.addListener({ // Используется для привязки жизненного цикла камер к владельцу жизненного цикла val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get() // Предварительный просмотр val предварительный просмотр = Preview.Builder() .строить() .также { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) } imageCapture = ImageCapture.Builder().build() // Выбираем заднюю камеру по умолчанию val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA пытаться { // Отвязываем варианты использования перед повторной привязкой cameraProvider.unbindAll() // Привязываем варианты использования к камере cameraProvider.bindToLifecycle( это, cameraSelector, предварительный просмотр, imageCapture) } catch (исключение: исключение) { Log.e(TAG, «Ошибка привязки варианта использования», exc) } }, ContextCompat.getMainExecutor(это)) } частное развлечение requestPermissions() { ActivityResultLauncher.launch(REQUIRED_PERMISSIONS) } личное развлечение allPermissionsGranted() = REQUIRED_PERMISSIONS.all { ContextCompat.checkSelfPermission( baseContext, это) == PackageManager.PERMISSION_GRANTED } переопределить удовольствие onDestroy() { супер.onDestroy() камераExecutor.shutdown() } сопутствующий объект { частный const val TAG = "CameraXApp" Private const val FILENAME_FORMAT = "гггг-ММ-дд-ЧЧ-мм-сс-ССС" личное значение REQUIRED_PERMISSIONS = изменяемыйСписок( Манифест.разрешение.КАМЕРА, Манифест.permission.RECORD_AUDIO ).применять { если (Build.VERSION.SDK_INT // Обработка разрешения предоставлено/отклонено вар разрешениеGranted = true разрешения.entries.forEach { if (it.key в REQUIRED_PERMISSIONS && it.value == false) разрешениеGranted = ложь } если (!permissionGranted) { Toast.makeText(baseContext, «Запрос на разрешение отклонен», Toast.LENGTH_SHORT).show() } еще { НачатьКамеру() } } }