You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android Camera2/CameraX相机输出被裁剪,如何显示完整视野?

相机预览与保存画面被裁剪问题解决方案

问题描述

开发的应用需展示相机预览并保存图片,但预览及保存的图片均仅显示相机可拍摄范围的一部分,画面明显被裁剪。目标环境为使用USB摄像头的嵌入式系统,在配置为webcam0的模拟器(连接笔记本USB摄像头)及生产环境中均出现该问题。尝试过Camera2与CameraX框架,问题一致。按照官方CameraX入门示例制作的应用也存在此问题:相机对准目标时仅显示一半画面,旋转模拟器后视野有所扩大但仍不完整,即使设置viewBinding.viewFinder.setScaleType(PreviewView.ScaleType.FIT_CENTER)也无效果。应用基于Java开发,现寻求配置图像捕获以显示相机完整视野的方案。

解决方案

1. 强制配置UseCase分辨率为摄像头支持的最大分辨率

CameraX默认会选择与预览View匹配的最优分辨率,但USB摄像头可能存在固定输出分辨率,导致画面被裁剪。需为Preview和ImageCapture设置摄像头支持的最大分辨率,同时保持两者分辨率一致,避免适配裁剪。

Kotlin代码修改(对应示例中的startCamera方法):

private fun startCamera() {
    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener({
        val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
        val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
        val cameraInfo = cameraProvider.getCameraInfo(cameraSelector)

        // 获取摄像头支持的所有分辨率,筛选出最大的那个
        val supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW)
        val maxResolution = supportedResolutions.maxByOrNull { it.width * it.height }

        // 配置Preview UseCase
        val preview = Preview.Builder()
            .apply {
                maxResolution?.let { setTargetResolution(it) }
                // 设置目标旋转,匹配屏幕方向
                setTargetRotation(viewBinding.viewFinder.display.rotation)
            }
            .build()
            .also { it.setSurfaceProvider(viewBinding.viewFinder.surfaceProvider) }

        // 配置ImageCapture UseCase,与Preview使用相同分辨率
        imageCapture = ImageCapture.Builder()
            .apply {
                maxResolution?.let { setTargetResolution(it) }
                // 选择最小延迟模式,优先保留完整视野而非画质
                setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                setTargetRotation(viewBinding.viewFinder.display.rotation)
            }
            .build()

        try {
            cameraProvider.unbindAll()
            cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture)
        } catch(exc: Exception) {
            Log.e(TAG, "Use case binding failed", exc)
        }

    }, ContextCompat.getMainExecutor(this))
}

Java对应实现:

private void startCamera() {
    ListenableFuture<ProcessCameraProvider> cameraProviderFuture = ProcessCameraProvider.getInstance(this);

    cameraProviderFuture.addListener(() -> {
        try {
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();
            CameraSelector cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA;
            CameraInfo cameraInfo = cameraProvider.getCameraInfo(cameraSelector);

            // 获取并筛选最大分辨率
            List<Size> supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW);
            Size maxResolution = null;
            for (Size size : supportedResolutions) {
                if (maxResolution == null || size.getWidth() * size.getHeight() > maxResolution.getWidth() * maxResolution.getHeight()) {
                    maxResolution = size;
                }
            }

            // 配置Preview
            Preview preview = new Preview.Builder()
                    .apply(builder -> {
                        if (maxResolution != null) {
                            builder.setTargetResolution(maxResolution);
                        }
                        builder.setTargetRotation(viewBinding.viewFinder.getDisplay().getRotation());
                    })
                    .build();
            preview.setSurfaceProvider(viewBinding.viewFinder.getSurfaceProvider());

            // 配置ImageCapture
            ImageCapture imageCapture = new ImageCapture.Builder()
                    .apply(builder -> {
                        if (maxResolution != null) {
                            builder.setTargetResolution(maxResolution);
                        }
                        builder.setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY);
                        builder.setTargetRotation(viewBinding.viewFinder.getDisplay().getRotation());
                    })
                    .build();

            // 绑定生命周期与UseCases
            cameraProvider.unbindAll();
            cameraProvider.bindToLifecycle(this, cameraSelector, preview, imageCapture);

        } catch (ExecutionException | InterruptedException e) {
            Log.e(TAG, "Use case binding failed", e);
        }
    }, ContextCompat.getMainExecutor(this));
}

2. 正确设置PreviewView的ScaleType与布局

确保PreviewView的布局占满容器,并在相机启动前设置ScaleType,避免Surface初始化后设置无效。

布局文件调整:

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

Kotlin中提前设置ScaleType:

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    viewBinding = ActivityMainBinding.inflate(layoutInflater)
    setContentView(viewBinding.root)

    // 先设置ScaleType再启动相机
    viewBinding.viewFinder.scaleType = PreviewView.ScaleType.FIT_CENTER

    // 权限请求与相机启动逻辑...
}

Java中提前设置ScaleType:

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    viewBinding = ActivityMainBinding.inflate(getLayoutInflater());
    setContentView(viewBinding.getRoot());

    // 提前设置ScaleType
    viewBinding.viewFinder.setScaleType(PreviewView.ScaleType.FIT_CENTER);

    // 权限请求与相机启动逻辑...
}

3. 验证摄像头支持的分辨率

通过日志打印所有支持的分辨率,确认是否存在合适的选项,也可根据需求选择与屏幕比例匹配的分辨率:

Kotlin日志打印:

val supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW)
supportedResolutions.forEach {
    Log.d(TAG, "Supported resolution: ${it.width}x${it.height}")
}

Java日志打印:

List<Size> supportedResolutions = cameraInfo.getSupportedResolutions(UseCaseGroup.UseCaseType.PREVIEW);
for (Size size : supportedResolutions) {
    Log.d(TAG, "Supported resolution: " + size.getWidth() + "x" + size.getHeight());
}

内容的提问来源于stack exchange,提问作者steve

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.05 04:50:54