Как переключиться на фронтальную камеру на CameraX?

Я выполнил шаги, описанные здесь, чтобы настроить CameraX, и теперь я пытаюсь заставить работать кнопку камеры на передней панели.

Вот мой код настройки:

private lateinit var preview: Preview

private fun startCamera() {

    // Create configuration object for the viewfinder use case
    val previewConfig = PreviewConfig.Builder().apply {
        setLensFacing(CameraX.LensFacing.BACK)
    }.build()

    // Build the viewfinder use case
    preview = Preview(previewConfig)

    // Every time the viewfinder is updated, recompute layout
    preview.setOnPreviewOutputUpdateListener {

        // To update the SurfaceTexture, we have to remove it and re-add it
        val parent = viewFinder.parent as ViewGroup
        parent.removeView(viewFinder)
        parent.addView(viewFinder, 0)

        viewFinder.surfaceTexture = it.surfaceTexture
        updateTransform()
    }

    // Bind use cases to lifecycle
    CameraX.bindToLifecycle(this, preview)
}

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

private fun initSwitchButton(view: View) {
    switchButton = view.findViewById(R.id.switch_button)
    switchButton.setOnClickListener {
        val previewConfig = PreviewConfig.Builder().apply { setLensFacing(CameraX.LensFacing.FRONT) }.build()
        preview = Preview(previewConfig)
    }
}

Тем не менее, это не переключается на фронтальную камеру. Что мне не хватает?

5 ответов

С 2021 года обновление CameraX рендерило CameraX.LensFacingнепригодный для использования. Использовать CameraSelector вместо.

          private CameraSelector lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;

    private void flipCamera() {
        if (lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) lensFacing = CameraSelector.DEFAULT_BACK_CAMERA;
        else if (lensFacing == CameraSelector.DEFAULT_BACK_CAMERA) lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;
        startCamera();
    }

    private void startCamera() {
        ListenableFuture<ProcessCameraProvider> cameraFuture = ProcessCameraProvider.getInstance(requireContext());

        cameraFuture.addListener(() -> {
            imageCapture = new ImageCapture.Builder()
                .setTargetRotation(cameraPreview.getDisplay().getRotation())
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build();
            videoCapture = new VideoCapture.Builder().build();

        try {
            ProcessCameraProvider processCameraProvider = cameraFuture.get();
            Preview preview = new Preview.Builder().build();
            preview.setSurfaceProvider(cameraPreview.getSurfaceProvider());
            processCameraProvider.unbindAll(); 

            // lensFacing is used here
            processCameraProvider.bindToLifecycle(getViewLifecycleOwner(), lensFacing, imageCapture, videoCapture, preview);
        } catch (ExecutionException e) {
            e.printStackTrace();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        }, ContextCompat.getMainExecutor(requireContext()));
    }

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

Вот фрагмент кода, который работал для меня:

private var lensFacing = CameraX.LensFacing.BACK
private var imageCapture: ImageCapture? = null

@SuppressLint("RestrictedApi")
private fun startCamera() {
    bindCameraUseCases()

    // Listener for button used to switch cameras
    switchButton = view.findViewById(R.id.switch_button)
    switchButton.setOnClickListener {
        lensFacing = if (CameraX.LensFacing.FRONT == lensFacing) {
            CameraX.LensFacing.BACK
        } else {
            CameraX.LensFacing.FRONT
        }
        try {
            // Only bind use cases if we can query a camera with this orientation
            CameraX.getCameraWithLensFacing(lensFacing)
            bindCameraUseCases()
        } catch (exc: Exception) {
            // Do nothing
        }
    }
}

private fun bindCameraUseCases() {
    // Make sure that there are no other use cases bound to CameraX
    CameraX.unbindAll()

    val previewConfig = PreviewConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    val preview = Preview(previewConfig)

    val imageCaptureConfig = ImageCaptureConfig.Builder().apply {
        setLensFacing(lensFacing)
    }.build()
    imageCapture = ImageCapture(imageCaptureConfig)

    // Apply declared configs to CameraX using the same lifecycle owner
    CameraX.bindToLifecycle(this, preview, imageCapture)
}

Вот как я сделал свой

      private var defaultCameraFacing = CameraSelector.DEFAULT_BACK_CAMERA

   btnFlipCamera.setOnClickListener {
        Log.d("CameraFacing", defaultCameraFacing.toString())
        defaultCameraFacing = if(defaultCameraFacing == CameraSelector.DEFAULT_FRONT_CAMERA){
            CameraSelector.DEFAULT_BACK_CAMERA
        }else{
            CameraSelector.DEFAULT_FRONT_CAMERA
        }

        try {
            // Only bind use cases if we can query a camera with this orientation
            startCamera(defaultCameraFacing)
        } catch (exc: Exception) {
            // Do nothing
        }
    }

private fun startCamera(defaultCameraFacing: CameraSelector) {
    llPictureCaptured.visibility = View.GONE
    tvLocationLabel.visibility= View.GONE
    pgLoadingLocation.visibility = View.GONE
    openCamera.visibility = View.GONE
    llCameraControl.visibility = View.VISIBLE
    viewFinder.visibility = View.VISIBLE


    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        // Used to bind the lifecycle of cameras to the lifecycle owner
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

        // Preview
        val preview = Preview.Builder()
            .build()
            .also {
                it.setSurfaceProvider(viewFinder.surfaceProvider)
            }

        imageCapture = ImageCapture.Builder()
            .build()

        //set image analysis, i.e luminosity analysis
        val imageAnalyzer = ImageAnalysis.Builder()
            .build()
            .also {
                it.setAnalyzer(cameraExecutor, LuminosityAnalyzer { luma ->
                    Log.d(TAG, "Average luminosity: $luma")
                })
            }

        // Set camera facing
        val cameraSelector = defaultCameraFacing

        try {
            // Unbind use cases before rebinding
            cameraProvider.unbindAll()

            // Bind use cases to camera
            cameraProvider.bindToLifecycle(
                this, cameraSelector, preview, imageCapture, imageAnalyzer)

        } catch (exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }

    }, ContextCompat.getMainExecutor(this))
}
private LensFacing lensFacing = CameraX.LensFacing.BACK;
private ImageCapture imageCapture = null;
private Button switchButton;


@SuppressLint("RestrictedApi")
private void startCamera() {
    bindCameraUseCases();

    // Listener for button used to switch cameras
    switchButton = view.findViewById(R.id.switch_button);
    switchButton.setOnClickListener(v -> {
        lensFacing = lensFacing == LensFacing.FRONT ? LensFacing.BACK : LensFacing.FRONT;
        try {
            // Only bind use cases if we can query a camera with this orientation
            CameraX.getCameraWithLensFacing(lensFacing);
            bindCameraUseCases();
        } catch (CameraInfoUnavailableException e) {
            // Do nothing
        }
    });
}

private void bindCameraUseCases() {
    // Make sure that there are no other use cases bound to CameraX
    CameraX.unbindAll();

    PreviewConfig previewConfig = new PreviewConfig.Builder().
            setLensFacing(lensFacing)
            .build();
    Preview preview = new Preview(previewConfig);

    ImageCaptureConfig imageCaptureConfig = new ImageCaptureConfig.Builder()
            .setLensFacing(lensFacing)
            .build();
    imageCapture = new ImageCapture(imageCaptureConfig);

    // Apply declared configs to CameraX using the same lifecycle owner
    CameraX.bindToLifecycle(this, preview, imageCapture);
}

Версия Java

Вот версия ответа @Rig на Kotlin . PreviewViewсоздается в XML-файле, а его идентификаторviewFinder, например.binding.viewFinder:

XML:

      <androidx.camera.view.PreviewView
    android:id="@+id/viewFinder"
    // ...
/>

Активность:

      var imageCapture: ImageCapture? = null
var lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA

fun toggleCamera() {

    if (lensFacing == CameraSelector.DEFAULT_FRONT_CAMERA) lensFacing = CameraSelector.DEFAULT_BACK_CAMERA;
    else if (lensFacing == CameraSelector.DEFAULT_BACK_CAMERA) lensFacing = CameraSelector.DEFAULT_FRONT_CAMERA;

    startCamera()
}

fun startCamera() {

    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({

        imageCapture = ImageCapture.Builder()
            .setTargetRotation(binding.viewFinder.display.rotation)
            .setCaptureMode(ImageCapture.CAPTURE_MODE_MAXIMIZE_QUALITY)
            .build()

        try {

            val processCameraProvider: ProcessCameraProvider = cameraProviderFuture.get()

            val preview = Preview.Builder().build()
            preview.setSurfaceProvider(binding.viewFinder.surfaceProvider)
            processCameraProvider.unbindAll()

            // lensFacing is used here
            processCameraProvider.bindToLifecycle(
                this,
                lensFacing,
                imageCapture,
                preview
            )

        } catch (e: ExecutionException) {
            Log.e(TAG, "Use case binding failed -1: ", e)
        } catch (e: InterruptedException) {
            Log.e(TAG, "Use case binding failed -2: ", e)
        }

    }, ContextCompat.getMainExecutor(this))
}
Другие вопросы по тегам